From a7be4d9c27d5a6074f3a1701b12bcc2c45887022 Mon Sep 17 00:00:00 2001 From: Jose Dapena Paz Date: Tue, 30 Sep 2008 17:10:22 +0000 Subject: [PATCH] * Added hildon2 directory, with a copy of the contents of maemo directory. * For hildon2, we removed the easysetup directory to have all the hildon2 ui in the same place. * Modified libalarm, to use the new API in fremantle. pmo-trunk-r5823 --- configure.ac | 23 +- libmodest-dbus-client/Makefile.am | 2 +- src/Makefile.am | 7 +- src/hildon2/Makefile.am | 142 + src/hildon2/com.nokia.modest.service.in | 3 + src/hildon2/maemo-provider-data.keyfile | 90 + src/hildon2/mcc_mapping | 230 ++ src/hildon2/mcc_mapping.en_GB | 107 + src/hildon2/modest-account-view-window.c | 540 +++ src/hildon2/modest-address-book.c | 996 ++++++ .../modest-connection-specific-smtp-edit-window.c | 525 +++ .../modest-connection-specific-smtp-edit-window.h | 86 + .../modest-connection-specific-smtp-window.c | 581 ++++ .../modest-connection-specific-smtp-window.h | 84 + .../modest-default-account-settings-dialog.c | 1566 +++++++++ src/hildon2/modest-easysetup-country-combo-box.c | 394 +++ src/hildon2/modest-easysetup-country-combo-box.h | 93 + src/hildon2/modest-easysetup-provider-combo-box.c | 332 ++ src/hildon2/modest-easysetup-provider-combo-box.h | 96 + .../modest-easysetup-servertype-combo-box.c | 276 ++ .../modest-easysetup-servertype-combo-box.h | 79 + src/hildon2/modest-easysetup-wizard-dialog.c | 2101 +++++++++++ src/hildon2/modest-easysetup-wizard-dialog.h | 93 + src/hildon2/modest-hildon-includes.h | 156 + src/hildon2/modest-hildon-sort-dialog.c | 133 + src/hildon2/modest-hildon-sort-dialog.h | 77 + src/hildon2/modest-icon-names.h | 113 + src/hildon2/modest-maemo-global-settings-dialog.c | 523 +++ src/hildon2/modest-maemo-global-settings-dialog.h | 64 + src/hildon2/modest-maemo-security-options-view.c | 488 +++ src/hildon2/modest-maemo-security-options-view.h | 69 + src/hildon2/modest-maemo-utils.c | 290 ++ src/hildon2/modest-maemo-utils.h | 126 + src/hildon2/modest-main-window-ui-dimming.h | 128 + src/hildon2/modest-main-window-ui.h | 136 + src/hildon2/modest-main-window.c | 3104 +++++++++++++++++ src/hildon2/modest-msg-edit-window-ui-dimming.h | 50 + src/hildon2/modest-msg-edit-window.c | 3679 ++++++++++++++++++++ src/hildon2/modest-msg-view-window-ui-dimming.h | 76 + src/hildon2/modest-msg-view-window.c | 3200 +++++++++++++++++ src/hildon2/modest-osso-autosave-callbacks.c | 40 + src/hildon2/modest-osso-autosave-callbacks.h | 40 + src/hildon2/modest-osso-state-saving.c | 51 + src/hildon2/modest-osso-state-saving.h | 40 + src/hildon2/modest-platform.c | 2118 +++++++++++ src/hildon2/modest-presets.c | 382 ++ src/hildon2/modest-presets.h | 187 + src/hildon2/modest-signature-editor-dialog.c | 232 ++ src/hildon2/modest-signature-editor-dialog.h | 85 + src/hildon2/modest.conf | 5 + src/hildon2/modest.desktop.in | 27 + src/hildon2/pixmaps/modest-icon.png | Bin 0 -> 1189 bytes src/hildon2/ui/modest-main-window-ui.xml | 151 + src/hildon2/ui/modest-msg-edit-window-ui.xml | 123 + src/hildon2/ui/modest-msg-view-window-ui.xml | 119 + src/modest-ui-actions.c | 4 + src/widgets/Makefile.am | 2 +- tests/Makefile.am | 4 +- tests/dbus_api/Makefile.am | 2 +- 59 files changed, 24452 insertions(+), 18 deletions(-) create mode 100644 src/hildon2/Makefile.am create mode 100644 src/hildon2/com.nokia.modest.service.in create mode 100644 src/hildon2/maemo-provider-data.keyfile create mode 100644 src/hildon2/mcc_mapping create mode 100644 src/hildon2/mcc_mapping.en_GB create mode 100644 src/hildon2/modest-account-view-window.c create mode 100644 src/hildon2/modest-address-book.c create mode 100644 src/hildon2/modest-connection-specific-smtp-edit-window.c create mode 100644 src/hildon2/modest-connection-specific-smtp-edit-window.h create mode 100644 src/hildon2/modest-connection-specific-smtp-window.c create mode 100644 src/hildon2/modest-connection-specific-smtp-window.h create mode 100644 src/hildon2/modest-default-account-settings-dialog.c create mode 100644 src/hildon2/modest-easysetup-country-combo-box.c create mode 100644 src/hildon2/modest-easysetup-country-combo-box.h create mode 100644 src/hildon2/modest-easysetup-provider-combo-box.c create mode 100644 src/hildon2/modest-easysetup-provider-combo-box.h create mode 100644 src/hildon2/modest-easysetup-servertype-combo-box.c create mode 100644 src/hildon2/modest-easysetup-servertype-combo-box.h create mode 100644 src/hildon2/modest-easysetup-wizard-dialog.c create mode 100644 src/hildon2/modest-easysetup-wizard-dialog.h create mode 100644 src/hildon2/modest-hildon-includes.h create mode 100644 src/hildon2/modest-hildon-sort-dialog.c create mode 100644 src/hildon2/modest-hildon-sort-dialog.h create mode 100644 src/hildon2/modest-icon-names.h create mode 100644 src/hildon2/modest-maemo-global-settings-dialog.c create mode 100644 src/hildon2/modest-maemo-global-settings-dialog.h create mode 100644 src/hildon2/modest-maemo-security-options-view.c create mode 100644 src/hildon2/modest-maemo-security-options-view.h create mode 100644 src/hildon2/modest-maemo-utils.c create mode 100644 src/hildon2/modest-maemo-utils.h create mode 100644 src/hildon2/modest-main-window-ui-dimming.h create mode 100644 src/hildon2/modest-main-window-ui.h create mode 100644 src/hildon2/modest-main-window.c create mode 100644 src/hildon2/modest-msg-edit-window-ui-dimming.h create mode 100644 src/hildon2/modest-msg-edit-window.c create mode 100644 src/hildon2/modest-msg-view-window-ui-dimming.h create mode 100644 src/hildon2/modest-msg-view-window.c create mode 100644 src/hildon2/modest-osso-autosave-callbacks.c create mode 100644 src/hildon2/modest-osso-autosave-callbacks.h create mode 100644 src/hildon2/modest-osso-state-saving.c create mode 100644 src/hildon2/modest-osso-state-saving.h create mode 100644 src/hildon2/modest-platform.c create mode 100644 src/hildon2/modest-presets.c create mode 100644 src/hildon2/modest-presets.h create mode 100644 src/hildon2/modest-signature-editor-dialog.c create mode 100644 src/hildon2/modest-signature-editor-dialog.h create mode 100644 src/hildon2/modest.conf create mode 100644 src/hildon2/modest.desktop.in create mode 100644 src/hildon2/pixmaps/modest-icon.png create mode 100644 src/hildon2/ui/modest-main-window-ui.xml create mode 100644 src/hildon2/ui/modest-msg-edit-window-ui.xml create mode 100644 src/hildon2/ui/modest-msg-view-window-ui.xml diff --git a/configure.ac b/configure.ac index cf443cd..8efe412 100644 --- a/configure.ac +++ b/configure.ac @@ -247,7 +247,7 @@ else PKG_CHECK_MODULES(MODEST_TOOLKIT,$hildonpkgs) if test "$with_toolkit" = "hildon2"; then AC_DEFINE_UNQUOTED(MODEST_TOOLKIT_HILDON2, 1, ["Whether modest is being build for the hildon2 toolkit."]) - MODEST_TOOLKIT_DIR=maemo + MODEST_TOOLKIT_DIR=hildon2 else AC_DEFINE_UNQUOTED(MODEST_TOOLKIT_HILDON1, 1, ["Whether modest is being build for the hildon2 toolkit."]) MODEST_TOOLKIT_DIR=maemo @@ -296,15 +296,15 @@ fi # check libalarm # if test "x$with_platform" == "xmaemo"; then -PKG_CHECK_MODULES(MODEST_LIBALARM,libalarm,have_libalarm=true,have_libalarm=false) -if test "x$have_libalarm" == "xtrue"; then - libalarm="libalarm" - AC_DEFINE_UNQUOTED(MODEST_HAVE_LIBALARM, 1, ["Whether libalarm is used."]) - AC_SUBST(MODEST_LIBALARM_CFLAGS) - AC_SUBST(MODEST_LIBALARM_LIBS) -else - libalarm="" -fi + PKG_CHECK_MODULES(MODEST_LIBALARM,libalarm,have_libalarm=true,have_libalarm=false) + if test "x$have_libalarm" == "xfalse"; then + PKG_CHECK_MODULES(MODEST_LIBALARM, alarm, have_libalarm=true,have_libalarm=false) + fi + if test "x$have_libalarm" == "xtrue"; then + AC_DEFINE_UNQUOTED(MODEST_HAVE_LIBALARM, 1, ["Whether libalarm is used."]) + AC_SUBST(MODEST_LIBALARM_CFLAGS) + AC_SUBST(MODEST_LIBALARM_LIBS) + fi fi # @@ -560,6 +560,7 @@ src/dbus_api/Makefile src/gnome/Makefile src/maemo/Makefile src/maemo/easysetup/Makefile +src/hildon2/Makefile src/widgets/Makefile docs/Makefile docs/reference/Makefile @@ -567,6 +568,8 @@ tests/Makefile tests/dbus_api/Makefile src/maemo/modest.desktop src/maemo/com.nokia.modest.service +src/hildon2/modest.desktop +src/hildon2/com.nokia.modest.service libmodest-dbus-client/Makefile libmodest-dbus-client/libmodest-dbus-client-1.0.pc ]) diff --git a/libmodest-dbus-client/Makefile.am b/libmodest-dbus-client/Makefile.am index 2fe3606..141bfc4 100644 --- a/libmodest-dbus-client/Makefile.am +++ b/libmodest-dbus-client/Makefile.am @@ -31,7 +31,7 @@ INCLUDES=\ $(MODEST_LIBTINYMAIL_GNOME_DESKTOP_CFLAGS) \ $(MODEST_LIBTINYMAIL_MAEMO_CFLAGS) \ $(MODEST_PLATFORM_CFLAGS) \ - -I$(MODEST_PLATFORM) \ + -I$(MODEST_TOOLKIT_DIR) \ -I$(top_srcdir)/src \ -DPREFIX=\"@prefix@\" \ -DMODEST_LOCALEDIR=\"$(MODEST_LOCALEDIR)\" \ diff --git a/src/Makefile.am b/src/Makefile.am index 379915e..993d1ba 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -3,7 +3,7 @@ # Time-stamp: <2008-01-06 10:18:49 (djcb)> SUBDIRS= $(MODEST_TOOLKIT_DIR) widgets $(dbus_api) -DIST_SUBDIRS = widgets gnome maemo dbus_api +DIST_SUBDIRS = widgets gnome maemo hildon2 dbus_api INCLUDES=\ $(MODEST_GSTUFF_CFLAGS) \ @@ -15,11 +15,12 @@ INCLUDES=\ $(MODEST_HILDON_NOTIFY_CFLAGS) \ $(MODEST_MOZEMBED_CFLAGS) \ -fPIC \ - -I$(MODEST_PLATFORM) \ + -I$(MODEST_TOOLKIT_DIR) \ -DPREFIX=\"@prefix@\" \ -DMAEMO_CHANGES \ -DMODEST_LOCALEDIR=\"$(MODEST_LOCALEDIR)\" \ -DMODEST_PLATFORM=\"$(MODEST_PLATFORM)\" \ + -DMODEST_TOOLKIT=\"$(MODEST_TOOLKIT)\" \ -DPIXMAP_PREFIX=\"${datadir}/pixmaps/modest/\" \ -DMODEST_UIDIR=\"$(datadir)/modest/ui/\" \ -DMODEST_MAILPLUGINDIR=\"$(libdir)/modest/mail-plugins\" @@ -169,7 +170,7 @@ libmodest_la_LIBADD = \ $(MODEST_OGS_LIBS) \ ${easysetupmaybe} \ widgets/libmodest-widgets.la \ - $(MODEST_PLATFORM)/libmodest-ui.la + $(MODEST_TOOLKIT_DIR)/libmodest-ui.la libmodest_la_LDFLAGS = -rdynamic -export-dynamic -no-undefined -Wl,--version-script=export.map if MODEST_HAVE_DBUS_API diff --git a/src/hildon2/Makefile.am b/src/hildon2/Makefile.am new file mode 100644 index 0000000..d35d0c4 --- /dev/null +++ b/src/hildon2/Makefile.am @@ -0,0 +1,142 @@ +# Copyright (c) 2006,2007 Nokia Corporation +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the Nokia Corporation nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +# OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +INCLUDES=\ + $(MODEST_GSTUFF_CFLAGS) \ + $(MODEST_WPEDITOR_CFLAGS) \ + $(MODEST_PLATFORM_CFLAGS) \ + $(MODEST_TOOLKIT_CFLAGS) \ + $(MODEST_ABOOK_CFLAGS) \ + $(MODEST_HILDON_HELP_CFLAGS) \ + $(MODEST_LIBCONIC_CFLAGS) \ + $(MODEST_HILDON_MIME_CFLAGS) \ + $(MODEST_HILDON_NOTIFY_CFLAGS) \ + $(MODEST_LIBALARM_CFLAGS) \ + -I ${top_srcdir}/src/widgets \ + -I ${top_srcdir}/src \ + -DPREFIX=\"@prefix@\" \ + -DMAEMO_CHANGES \ + -DPIXMAP_PREFIX=\"${datadir}/pixmaps/modest/\" \ + -DMODEST_UIDIR=\""$(datadir)/modest/ui/"\" \ + -Wall + +noinst_LTLIBRARIES=\ + libmodest-ui.la + +libmodest_ui_la_SOURCES= \ + modest-account-view-window.c \ + modest-default-account-settings-dialog.c \ + modest-easysetup-wizard-dialog.h modest-easysetup-wizard-dialog.c \ + modest-easysetup-country-combo-box.h modest-easysetup-country-combo-box.c \ + modest-easysetup-provider-combo-box.h modest-easysetup-provider-combo-box.c \ + modest-easysetup-servertype-combo-box.h modest-easysetup-servertype-combo-box.c \ + modest-icon-names.h \ + modest-maemo-global-settings-dialog.c \ + modest-maemo-global-settings-dialog.h \ + modest-maemo-security-options-view.c \ + modest-main-window.c \ + modest-main-window-ui.h \ + modest-hildon-includes.h \ + modest-hildon-sort-dialog.c \ + modest-hildon-sort-dialog.h \ + modest-platform.c \ + modest-signature-editor-dialog.c \ + modest-signature-editor-dialog.h \ + modest-msg-view-window.c \ + modest-msg-edit-window.c \ + modest-maemo-utils.c \ + modest-connection-specific-smtp-window.h \ + modest-connection-specific-smtp-window.c \ + modest-connection-specific-smtp-edit-window.h \ + modest-connection-specific-smtp-edit-window.c \ + modest-osso-autosave-callbacks.c \ + modest-osso-autosave-callbacks.h \ + modest-osso-state-saving.c \ + modest-osso-state-saving.h \ + modest-presets.h modest-presets.c + +if MODEST_USE_DUMMY_ADDRESS_BOOK +# nothing +else +libmodest_ui_la_SOURCES += modest-address-book.c +endif + + +libmodest_ui_la_LIBADD = \ + $(MODEST_GSTUFF_LIBS) \ + $(MODEST_ABOOK_LIBS) \ + $(MODEST_PLATFORM_LIBS) \ + $(MODEST_TOOLKIT_LIBS) \ + $(MODEST_WPEDITOR_LIBS) \ + $(MODEST_HILDON_MIME_LIBS) \ + $(MODEST_LIBCONIC_LIBS) \ + $(MODEST_HILDON_HELP_LIBS) \ + $(MODEST_LIBALARM_LIBS) \ + $(MODEST_HILDON_NOTIFY_LIBS) + +PIXMAP_FILES=\ + pixmaps/*.png + +pixmapdir = $(datadir)/pixmaps/modest +pixmap_DATA = $(PIXMAP_FILES) + + + +UI_FILES=\ + ui/modest-main-window-ui.xml \ + ui/modest-msg-edit-window-ui.xml \ + ui/modest-msg-view-window-ui.xml + +uidir = $(datadir)/modest/ui +ui_DATA = $(UI_FILES) + +serviceentry_DATA = com.nokia.modest.service +desktopentry_DATA = modest.desktop +backupsettings_DATA = modest.conf + +icon_26x26_DATA = pixmaps/modest-icon.png + +# mcc_mapping may be provided by the operator-wizard-settings package: +# the one here is a copy, as that package is not available in chinook +MMC_MAPPING_FILE=mcc_mapping +mmccmappingdir=$(datadir)/modest/provider-data +mmccmapping_DATA = $(MMC_MAPPING_FILE) + +PROVIDER_DATA_FILE=maemo-provider-data.keyfile +providerdatadir=$(datadir)/modest/provider-data +providerdata_DATA = $(PROVIDER_DATA_FILE) + +EXTRA_DIST=\ + $(UI_FILES) \ + $(PIXMAP_FILES) \ + $(serviceentry_DATA) \ + $(desktopentry_DATA) \ + $(backupsettings_DATA) \ + $(icon_26x26_DATA) \ + $(MMC_MAPPING_FILE) \ + $(PROVIDER_DATA_FILE) diff --git a/src/hildon2/com.nokia.modest.service.in b/src/hildon2/com.nokia.modest.service.in new file mode 100644 index 0000000..7ab396c --- /dev/null +++ b/src/hildon2/com.nokia.modest.service.in @@ -0,0 +1,3 @@ +[D-BUS Service] +Name=com.nokia.modest +Exec=@prefix@/bin/modest diff --git a/src/hildon2/maemo-provider-data.keyfile b/src/hildon2/maemo-provider-data.keyfile new file mode 100644 index 0000000..9152172 --- /dev/null +++ b/src/hildon2/maemo-provider-data.keyfile @@ -0,0 +1,90 @@ +# maemo-provider-data.keyfile -*-mode:sh-*- +# Time-stamp: <2007-12-01 13:39:11 (djcb)> + +# Copyright (c) 2006, Nokia Corporation +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the Nokia Corporation nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +# OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +[aim.com] +Name=AIM +MCC=0 +Domain=aim.com +OutgoingMailServer=smtp.aim.com +IncomingMailServer=imap.aim.com +IncomingSecurity=0 +MailboxType=imap +[aol.com] +Name=AOL +MCC=0 +Domain=aol.com +OutgoingMailServer=smtp.aol.com +IncomingMailServer=imap.aol.com +IncomingSecurity=0 +MailboxType=imap +[imap.gmail.com] +Name=Gmail/IMAP +MCC=0 +Domain=gmail.com +OutgoingMailServer=smtp.gmail.com +SecureSmtp=true +IncomingMailServer=imap.gmail.com +IncomingSecurity=2 +MailboxType=imap +[pop.gmail.com] +Name=Gmail/POP3 +MCC=0 +Domain=gmail.com +OutgoingMailServer=smtp.gmail.com +SecureSmtp=true +IncomingMailServer=pop.gmail.com +IncomingSecurity=2 +MailboxType=pop +[lycos.com] +Name=Lycos Mail +MCC=0 +Domain=lycos.com +OutgoingMailServer=smtp.mail.lycos.com +IncomingMailServer=pop.mail.lycos.com +IncomingSecurity=0 +MailboxType=pop +[yahoo.com] +Name=Yahoo Mail Plus +MCC=0 +Domain=yahoo.com +OutgoingMailServer=smtp.mail.yahoo.com +IncomingMailServer=pop.mail.yahoo.com +IncomingSecurity=0 +MailboxType=pop +[Tinymail] +Name=Tinymail +MCC=206 +Domain=tinymail.org +IncomingMailServer=imap1.tinymail.org +IncomingSecurity=0 +MailboxType=imap + diff --git a/src/hildon2/mcc_mapping b/src/hildon2/mcc_mapping new file mode 100644 index 0000000..6b984d1 --- /dev/null +++ b/src/hildon2/mcc_mapping @@ -0,0 +1,230 @@ +412 osso_db_country_afghanistan +276 osso_db_country_albania +603 osso_db_country_algeria +544 osso_db_country_american_samoa +213 osso_db_country_andorra +631 osso_db_country_angola +365 osso_db_country_anguilla +344 osso_db_country_antigua_and_barbuda +722 osso_db_country_argentina +283 osso_db_country_armenia +363 osso_db_country_aruba +505 osso_db_country_australia +232 osso_db_country_austria +400 osso_db_country_azerbaijani_republic +364 osso_db_country_bahamas +426 osso_db_country_bahrain +470 osso_db_country_bangladesh +342 osso_db_country_barbados +257 osso_db_country_belarus +206 osso_db_country_belgium +702 osso_db_country_belize +616 osso_db_country_benin +350 osso_db_country_bermuda +402 osso_db_country_bhutan +736 osso_db_country_bolivia +218 osso_db_country_bosnia_and_herzegovina +652 osso_db_country_botswana +724 osso_db_country_brazil +348 osso_db_country_british_virgin_islands +528 osso_db_country_brunei_darussalam +284 osso_db_country_bulgaria +613 osso_db_country_burkina_faso +642 osso_db_country_burundi +456 osso_db_country_cambodia +624 osso_db_country_cameroon +302 osso_db_country_canada +625 osso_db_country_cape_verde +346 osso_db_country_cayman_islands +623 osso_db_country_central_african_republic +622 osso_db_country_chad +730 osso_db_country_chile +461 osso_db_country_china +460 osso_db_country_china +732 osso_db_country_colombia +654 osso_db_country_comoros +629 osso_db_country_republic_of_the_congo +548 osso_db_country_cook_islands +712 osso_db_country_costa_rica +612 osso_db_country_cote_dlvoire +219 osso_db_country_croatia +368 osso_db_country_cuba +280 osso_db_country_cyprus +230 osso_db_country_czech_republic +630 osso_db_country_democratic_republic_of_the_congo +238 osso_db_country_denmark +638 osso_db_country_djibouti +366 osso_db_country_dominica +370 osso_db_country_dominican_republic +514 osso_db_country_east_timor +740 osso_db_country_ecuador +602 osso_db_country_egypt +706 osso_db_country_el_salvador +627 osso_db_country_equatorial_guinea +657 osso_db_country_eritrea +248 osso_db_country_estonia +636 osso_db_country_ethiopia +288 osso_db_country_faroe_islands +542 osso_db_country_fiji +244 osso_db_country_finland +208 osso_db_country_france +742 osso_db_country_french_guiana +547 osso_db_country_french_polynesia +628 osso_db_country_gobonese_republic +607 osso_db_country_gambia +282 osso_db_country_georgia +262 osso_db_country_germany +620 osso_db_country_ghana +266 osso_db_country_gibraltar +202 osso_db_country_greece +290 osso_db_country_greenland +352 osso_db_country_grenada +340 osso_db_country_guadeloupe +535 osso_db_country_guam +704 osso_db_country_guatemala +611 osso_db_country_guinea +632 osso_db_country_guinea_bissaus +738 osso_db_country_guyana +372 osso_db_country_haiti +708 osso_db_country_honduras +454 osso_db_country_hong_kong +216 osso_db_country_hungary +274 osso_db_country_iceland +404 osso_db_country_india +510 osso_db_country_indonesia +432 osso_db_country_iran +418 osso_db_country_iraq +272 osso_db_country_republic_of_ireland +425 osso_db_country_israel +222 osso_db_country_italy +338 osso_db_country_jamaica +441 osso_db_country_japan +440 osso_db_country_japan +416 osso_db_country_jordan +401 osso_db_country_kazakhstan +639 osso_db_country_kenya +545 osso_db_country_kiribati +467 osso_db_country_korea_north +450 osso_db_country_country_south_korea +419 osso_db_country_kuwait +437 osso_db_country_kyrgyz_republic +457 osso_db_country_laos +247 osso_db_country_latvia +415 osso_db_country_lebanon +651 osso_db_country_lesotho +618 osso_db_country_liberia +606 osso_db_country_libya +295 osso_db_country_liechtenstein +246 osso_db_country_lithuania +270 osso_db_country_luxembourg +455 osso_db_country_macao +294 osso_db_country_the_former_yugoslav_republic_of_macedonia +646 osso_db_country_madagascar +650 osso_db_country_malawi +502 osso_db_country_malaysia +472 osso_db_country_maldives +610 osso_db_country_mali +278 osso_db_country_malta +551 osso_db_country_marshall_islands +340 osso_db_country_martinique +609 osso_db_country_mauritania +617 osso_db_country_mauritius +334 osso_db_country_mexico +550 osso_db_country_micronesia +259 osso_db_country_republic_of_moldova +212 osso_db_country_monaco +428 osso_db_country_mongolia +354 osso_db_country_montserrat +604 osso_db_country_morocco +643 osso_db_country_mozambique +414 osso_db_country_myanmar +649 osso_db_country_namibia +536 osso_db_country_nauru +429 osso_db_country_nepal +204 osso_db_country_netherlands +362 osso_db_country_netherlands_antilles +546 osso_db_country_new_caledonia +530 osso_db_country_new_zealand +710 osso_db_country_nicaraqua +614 osso_db_country_niger +621 osso_db_country_nigeria +534 osso_db_country_northern_mariana_islands +242 osso_db_country_norway +422 osso_db_country_oman +410 osso_db_country_pakistan +552 osso_db_country_palau +714 osso_db_country_panama +537 osso_db_country_papua_new_guinea +744 osso_db_country_paraquay +716 osso_db_country_peru +515 osso_db_country_philippines +260 osso_db_country_poland +268 osso_db_country_portugal +330 osso_db_country_puerto_rico +427 osso_db_country_qatar +647 osso_db_country_reunion +226 osso_db_country_romania +250 osso_db_country_russian_federation +635 osso_db_country_rwandese_republic +356 osso_db_country_saint_kitts_and_nevis +358 osso_db_country_saint_lucia +308 osso_db_country_saint_pierre_and_miquelon +360 osso_db_country_saint_vincent_and_grenadines +549 osso_db_country_samoa +292 osso_db_country_san_marino +626 osso_db_country_sao_tome_and_principe +420 osso_db_country_saudi_arabia +608 osso_db_country_senegal +220 osso_db_country_serbia_and_montenegro +633 osso_db_country_seychelles +619 osso_db_country_sierra_leone +525 osso_db_country_singapore +231 osso_db_country_slovakia +293 osso_db_country_slovenia +540 osso_db_country_solomon_islands +637 osso_db_country_somalia +655 osso_db_country_south_africa +214 osso_db_country_spain +413 osso_db_country_sri_lanka +634 osso_db_country_sudan +746 osso_db_country_suriname +653 osso_db_country_swaziland +240 osso_db_country_sweden +228 osso_db_country_switzerland +417 osso_db_country_syrian_arab_republic +466 osso_db_country_taiwan +436 osso_db_country_tajikistan +640 osso_db_country_united_republic_of_tanzania +520 osso_db_country_thailand +615 osso_db_country_togolese_republic +539 osso_db_country_tonga +374 osso_db_country_trinidad_and_tobago +605 osso_db_country_tunisia +286 osso_db_country_turkey +438 osso_db_country_turkmenistan +376 osso_db_country_turks_and_caicos_islands +641 osso_db_country_uganda +255 osso_db_country_ukraine +424 osso_db_country_united_arab_emirates +430 osso_db_country_united_arab_emirates_abu_dhabi +431 osso_db_country_united_arab_emirates_dubai +235 osso_db_country_united_kingdom_of_great_britain_and_northern_ireland +234 osso_db_country_united_kingdom_of_great_britain_and_northern_ireland +310 osso_db_country_united_states_of_america +316 osso_db_country_united_states_of_america +311 osso_db_country_united_states_of_america +312 osso_db_country_united_states_of_america +313 osso_db_country_united_states_of_america +314 osso_db_country_united_states_of_america +315 osso_db_country_united_states_of_america +332 osso_db_country_unites_states_virgin_islands +748 osso_db_country_uruguay +434 osso_db_country_uzbekistan +541 osso_db_country_vanuatu +225 osso_db_country_the_vatican +734 osso_db_country_venezuela +452 osso_db_country_viet_nam +543 osso_db_country_wallis_and_futuna +421 osso_db_country_yemen +645 osso_db_country_zambia +648 osso_db_country_zimbabwe diff --git a/src/hildon2/mcc_mapping.en_GB b/src/hildon2/mcc_mapping.en_GB new file mode 100644 index 0000000..491e504 --- /dev/null +++ b/src/hildon2/mcc_mapping.en_GB @@ -0,0 +1,107 @@ +276 Albania +213 Andorra +722 Argentina +363 Aruba +505 Australia +232 Austria +400 Azerbaijan +426 Bahrain +470 Bangladesh +257 Belarus +206 Belgium +736 Bolivia +218 Bosnia and Herzegovina +652 Botswana +724 Brazil +528 Brunei Darussalam +284 Bulgaria +456 Cambodia +302 Canada +730 Chile +460 China +732 Colombia +219 Croatia +280 Cyprus +230 Czech Republic +238 Denmark +370 Dominican Republic +740 Ecuador +602 Egypt +706 El Salvador +248 Estonia +244 Finland +208 France +282 Georgia +262 Germany +620 Ghana +266 Gibraltar +202 Greece +340 Guadeloupe +704 Guatemala +708 Honduras +454 Hong Kong, China +216 Hungary +274 Iceland +404 India +510 Indonesia +272 Ireland +425 Israel +222 Italy +338 Jamaica +440 Japan +416 Jordan +401 Kazakhstan +639 Kenya +419 Kuwait +247 Latvia +415 Lebanon +295 Liechtenstein +246 Lithuania +270 Luxembourg +455 Macao, China +294 Macedonia +502 Malaysia +472 Maldives +278 Malta +340 Martinique +334 Mexico +259 Moldova +212 Monaco +428 Mongolia +220 Montenegro +604 Morocco +204 Netherlands +530 New Zealand +621 Nigeria +242 Norway +422 Oman +410 Pakistan +714 Panama +744 Paraguay +716 Peru +515 Philippines +260 Poland +268 Portugal +427 Qatar +226 Romania +250 Russian Federation +420 Saudi Arabia +220 Serbia +525 Singapore +231 Slovakia +293 Slovenia +655 South Africa +214 Spain +413 Sri Lanka +240 Sweden +228 Switzerland +417 Syria +466 Taiwan +520 Thailand +374 Trinidad and Tobago +286 Turkey +255 Ukraine +234 United Kingdom +748 Uruguay +310 USA +734 Venezuela diff --git a/src/hildon2/modest-account-view-window.c b/src/hildon2/modest-account-view-window.c new file mode 100644 index 0000000..8c7e36c --- /dev/null +++ b/src/hildon2/modest-account-view-window.c @@ -0,0 +1,540 @@ +/* Copyright (c) 2006, Nokia Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Nokia Corporation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include + +#include +#include "modest-platform.h" +#include "modest-account-protocol.h" +#include +#include +#include "modest-tny-platform-factory.h" +#include "modest-easysetup-wizard-dialog.h" +#include "modest-account-settings-dialog.h" +#include +#include "widgets/modest-ui-constants.h" + +/* 'private'/'protected' functions */ +static void modest_account_view_window_class_init (ModestAccountViewWindowClass *klass); +static void modest_account_view_window_init (ModestAccountViewWindow *obj); +static void modest_account_view_window_finalize (GObject *obj); + +/* list my signals */ +enum { + /* MY_SIGNAL_1, */ + /* MY_SIGNAL_2, */ + LAST_SIGNAL +}; + +typedef struct _ModestAccountViewWindowPrivate ModestAccountViewWindowPrivate; +struct _ModestAccountViewWindowPrivate { + GtkWidget *new_button; + GtkWidget *edit_button; + GtkWidget *delete_button; + GtkWidget *close_button; + ModestAccountView *account_view; + guint acc_removed_handler; +}; +#define MODEST_ACCOUNT_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \ + MODEST_TYPE_ACCOUNT_VIEW_WINDOW, \ + ModestAccountViewWindowPrivate)) +/* globals */ +static GtkDialogClass *parent_class = NULL; + +/* uncomment the following if you have defined any signals */ +/* static guint signals[LAST_SIGNAL] = {0}; */ + +GType +modest_account_view_window_get_type (void) +{ + static GType my_type = 0; + if (!my_type) { + static const GTypeInfo my_info = { + sizeof(ModestAccountViewWindowClass), + NULL, /* base init */ + NULL, /* base finalize */ + (GClassInitFunc) modest_account_view_window_class_init, + NULL, /* class finalize */ + NULL, /* class data */ + sizeof(ModestAccountViewWindow), + 1, /* n_preallocs */ + (GInstanceInitFunc) modest_account_view_window_init, + NULL + }; + my_type = g_type_register_static (GTK_TYPE_DIALOG, + "ModestAccountViewWindow", + &my_info, 0); + } + return my_type; +} + +static void +modest_account_view_window_class_init (ModestAccountViewWindowClass *klass) +{ + GObjectClass *gobject_class; + gobject_class = (GObjectClass*) klass; + + parent_class = g_type_class_peek_parent (klass); + gobject_class->finalize = modest_account_view_window_finalize; + + g_type_class_add_private (gobject_class, sizeof(ModestAccountViewWindowPrivate)); +} + +static void +modest_account_view_window_finalize (GObject *self) +{ + ModestAccountViewWindowPrivate *priv; + ModestAccountMgr *mgr; + + priv = MODEST_ACCOUNT_VIEW_WINDOW_GET_PRIVATE (self); + mgr = modest_runtime_get_account_mgr (); + + if (g_signal_handler_is_connected (mgr, priv->acc_removed_handler)) + g_signal_handler_disconnect (mgr, priv->acc_removed_handler); + priv->acc_removed_handler = 0; + + G_OBJECT_CLASS(parent_class)->finalize (self); +} + + +static void +on_selection_changed (GtkTreeSelection *sel, ModestAccountViewWindow *self) +{ + ModestAccountViewWindowPrivate *priv; + GtkTreeModel *model; + GtkTreeIter iter; + gboolean has_selection; + + priv = MODEST_ACCOUNT_VIEW_WINDOW_GET_PRIVATE(self); + + has_selection = + gtk_tree_selection_get_selected (sel, &model, &iter); + + /* Set the status of the buttons */ + gtk_widget_set_sensitive (priv->edit_button, has_selection); + gtk_widget_set_sensitive (priv->delete_button, has_selection); +} + +/** Check whether any connections are active, and cancel them if + * the user wishes. + * Returns TRUE is there was no problem, + * or if an operation was cancelled so we can continue. + * Returns FALSE if the user chose to cancel his request instead. + */ +static gboolean +check_for_active_account (ModestAccountViewWindow *self, const gchar* account_name) +{ + ModestTnySendQueue *send_queue; + ModestTnyAccountStore *acc_store; + ModestMailOperationQueue* queue; + TnyConnectionStatus store_conn_status; + TnyAccount *store_account = NULL, *transport_account = NULL; + gboolean retval = TRUE, sending = FALSE; + + acc_store = modest_runtime_get_account_store (); + queue = modest_runtime_get_mail_operation_queue (); + + store_account = + modest_tny_account_store_get_server_account (acc_store, + account_name, + TNY_ACCOUNT_TYPE_STORE); + + /* This could happen if the account was deleted before the + call to this function */ + if (!store_account) + return FALSE; + + transport_account = + modest_tny_account_store_get_server_account (acc_store, + account_name, + TNY_ACCOUNT_TYPE_TRANSPORT); + + /* This could happen if the account was deleted before the + call to this function */ + if (!transport_account) { + g_object_unref (store_account); + return FALSE; + } + + /* If the transport account was not used yet, then the send + queue could not exist (it's created on demand) */ + send_queue = modest_runtime_get_send_queue (TNY_TRANSPORT_ACCOUNT (transport_account), FALSE); + if (TNY_IS_SEND_QUEUE (send_queue)) + sending = modest_tny_send_queue_sending_in_progress (send_queue); + + store_conn_status = tny_account_get_connection_status (store_account); + if (store_conn_status == TNY_CONNECTION_STATUS_CONNECTED || sending) { + gint response; + + response = modest_platform_run_confirmation_dialog (GTK_WINDOW (self), + _("emev_nc_disconnect_account")); + if (response == GTK_RESPONSE_OK) { + retval = TRUE; + } else { + retval = FALSE; + } + } + + if (retval) { + + /* FIXME: We should only cancel those of this account */ + modest_mail_operation_queue_cancel_all (queue); + + /* Also disconnect the account */ + if ((tny_account_get_connection_status (store_account) != TNY_CONNECTION_STATUS_DISCONNECTED) && + (tny_account_get_connection_status (store_account) != TNY_CONNECTION_STATUS_DISCONNECTED_BROKEN)) { + tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (store_account), + FALSE, NULL, NULL); + } + if (sending) { + tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (transport_account), + FALSE, NULL, NULL); + } + } + + /* Frees */ + g_object_unref (store_account); + g_object_unref (transport_account); + + return retval; +} + +static void +on_delete_button_clicked (GtkWidget *button, ModestAccountViewWindow *self) +{ + ModestAccountViewWindowPrivate *priv; + ModestAccountMgr *account_mgr; + gchar *account_title = NULL, *account_name = NULL; + + priv = MODEST_ACCOUNT_VIEW_WINDOW_GET_PRIVATE(self); + + account_mgr = modest_runtime_get_account_mgr(); + account_name = modest_account_view_get_selected_account (priv->account_view); + if(!account_name) + return; + + account_title = modest_account_mgr_get_display_name(account_mgr, account_name); + /* This could happen if the account is being deleted */ + if (!account_title) + return; + + if (check_for_active_account (self, account_name)) { + /* The warning text depends on the account type: */ + gchar *txt = NULL; + gint response; + ModestProtocol *protocol; + + protocol = modest_protocol_registry_get_protocol_by_type (modest_runtime_get_protocol_registry (), + modest_account_mgr_get_store_protocol (account_mgr, account_name)); + txt = modest_protocol_get_translation (protocol, MODEST_PROTOCOL_TRANSLATION_DELETE_MAILBOX, account_title); + if (txt == NULL) { + txt = g_strdup_printf (_("emev_nc_delete_mailbox"), + account_title); + } + + response = modest_platform_run_confirmation_dialog (GTK_WINDOW (self), txt); + g_free (txt); + txt = NULL; + + if (response == GTK_RESPONSE_OK) { + /* Remove account. If it succeeds then it also removes + the account from the ModestAccountView: */ + gboolean is_default = FALSE; + gchar *default_account_name = modest_account_mgr_get_default_account (account_mgr); + if (default_account_name && (strcmp (default_account_name, account_name) == 0)) + is_default = TRUE; + g_free (default_account_name); + + gboolean removed = modest_account_mgr_remove_account (account_mgr, account_name); + if (!removed) { + g_warning ("%s: modest_account_mgr_remove_account() failed.\n", __FUNCTION__); + } + + } + g_free (account_title); + } + g_free (account_name); +} + +static void +on_account_settings_dialog_response (GtkDialog *dialog, + gint response, + gpointer user_data) +{ + TnyAccount *store_account = NULL; + gchar* account_name = NULL; + ModestAccountViewWindowPrivate *priv = NULL; + + priv = MODEST_ACCOUNT_VIEW_WINDOW_GET_PRIVATE (user_data); + account_name = modest_account_view_get_selected_account (priv->account_view); + store_account = modest_tny_account_store_get_server_account (modest_runtime_get_account_store (), + account_name, + TNY_ACCOUNT_TYPE_STORE); + + /* Reconnect the store account, no need to reconnect the + transport account because it will connect when needed */ + if (tny_account_get_connection_status (store_account) == + TNY_CONNECTION_STATUS_DISCONNECTED) + tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (store_account), + TRUE, NULL, NULL); + + /* Disconnect this handler */ + g_signal_handlers_disconnect_by_func (dialog, on_account_settings_dialog_response, user_data); + + /* Free */ + g_free (account_name); + g_object_unref (store_account); +} + +static void +on_edit_button_clicked (GtkWidget *button, ModestAccountViewWindow *self) +{ + ModestAccountViewWindowPrivate *priv = MODEST_ACCOUNT_VIEW_WINDOW_GET_PRIVATE (self); + + gchar* account_name = modest_account_view_get_selected_account (priv->account_view); + if (!account_name) + return; + + /* Check whether any connections are active, and cancel them if + * the user wishes. + */ + if (check_for_active_account (self, account_name)) { + ModestAccountProtocol *proto; + ModestProtocolType proto_type; + + /* Get proto */ + proto_type = modest_account_mgr_get_store_protocol (modest_runtime_get_account_mgr (), + account_name); + proto = (ModestAccountProtocol *) + modest_protocol_registry_get_protocol_by_type (modest_runtime_get_protocol_registry (), + proto_type); + + /* Create and show the dialog */ + if (proto && MODEST_IS_ACCOUNT_PROTOCOL (proto)) { + ModestAccountSettingsDialog *dialog = + modest_account_protocol_get_account_settings_dialog (proto, account_name); + gtk_widget_show (GTK_WIDGET (dialog)); + } + } + g_free (account_name); +} + +static void +on_wizard_response (GtkDialog *dialog, + gint response, + gpointer user_data) +{ + /* The response has already been handled by the wizard dialog itself, + * creating the new account. + */ + if (dialog) + gtk_widget_destroy (GTK_WIDGET (dialog)); + + /* Re-focus the account list view widget */ + if (MODEST_IS_ACCOUNT_VIEW_WINDOW (user_data)) { + ModestAccountViewWindowPrivate *priv; + priv = MODEST_ACCOUNT_VIEW_WINDOW_GET_PRIVATE (user_data); + gtk_widget_grab_focus (GTK_WIDGET (priv->account_view)); + } +} + +static void +on_new_button_clicked (GtkWidget *button, ModestAccountViewWindow *self) +{ + GtkDialog *wizard; + GtkWindow *dialog; + + /* Show the easy-setup wizard: */ + dialog = modest_window_mgr_get_modal (modest_runtime_get_window_mgr()); + if (dialog && MODEST_IS_EASYSETUP_WIZARD_DIALOG(dialog)) { + /* old wizard is active already; + */ + gtk_window_present (dialog); + return; + } + + /* there is no such wizard yet */ + wizard = GTK_DIALOG (modest_easysetup_wizard_dialog_new ()); + modest_window_mgr_set_modal (modest_runtime_get_window_mgr(), + GTK_WINDOW (wizard)); + + /* if there is already another modal dialog, make it non-modal */ + if (dialog) + gtk_window_set_modal (GTK_WINDOW(dialog), FALSE); + + gtk_window_set_modal (GTK_WINDOW (wizard), TRUE); + gtk_window_set_transient_for (GTK_WINDOW (wizard), GTK_WINDOW (self)); + /* Destroy the dialog when it is closed: */ + g_signal_connect (G_OBJECT (wizard), "response", G_CALLBACK + (on_wizard_response), self); + gtk_widget_show (GTK_WIDGET (wizard)); +} + +static void +on_close_button_clicked (GtkWidget *button, gpointer user_data) +{ + ModestAccountViewWindow *self = MODEST_ACCOUNT_VIEW_WINDOW (user_data); + + gtk_dialog_response (GTK_DIALOG (self), GTK_RESPONSE_OK); +} + +static void +setup_button_box (ModestAccountViewWindow *self, GtkButtonBox *box) +{ + ModestAccountViewWindowPrivate *priv = MODEST_ACCOUNT_VIEW_WINDOW_GET_PRIVATE(self); + + gtk_button_box_set_spacing (GTK_BUTTON_BOX (box), 6); + gtk_button_box_set_layout (GTK_BUTTON_BOX (box), + GTK_BUTTONBOX_START); + + priv->new_button = gtk_button_new_from_stock(_("mcen_bd_new")); + priv->edit_button = gtk_button_new_with_label(_("mcen_bd_edit")); + priv->delete_button = gtk_button_new_from_stock(_("mcen_bd_delete")); + priv->close_button = gtk_button_new_from_stock(_("mcen_bd_close")); + + g_signal_connect (G_OBJECT(priv->new_button), "clicked", + G_CALLBACK(on_new_button_clicked), + self); + g_signal_connect (G_OBJECT(priv->delete_button), "clicked", + G_CALLBACK(on_delete_button_clicked), + self); + g_signal_connect (G_OBJECT(priv->edit_button), "clicked", + G_CALLBACK(on_edit_button_clicked), + self); + g_signal_connect (G_OBJECT(priv->close_button), "clicked", + G_CALLBACK(on_close_button_clicked), + self); + + gtk_box_pack_start (GTK_BOX(box), priv->new_button, FALSE, FALSE,2); + gtk_box_pack_start (GTK_BOX(box), priv->edit_button, FALSE, FALSE,2); + gtk_box_pack_start (GTK_BOX(box), priv->delete_button, FALSE, FALSE,2); + gtk_box_pack_start (GTK_BOX(box), priv->close_button, FALSE, FALSE,2); + + /* Should has been created by window_vbox_new */ + if (priv->account_view) { + GtkTreeSelection *sel; + sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(priv->account_view)); + if (gtk_tree_selection_count_selected_rows (sel) == 0) { + gtk_widget_set_sensitive (priv->edit_button, FALSE); + gtk_widget_set_sensitive (priv->delete_button, FALSE); + } + } + + gtk_widget_show_all (GTK_WIDGET (box)); +} + +static GtkWidget* +window_vbox_new (ModestAccountViewWindow *self) +{ + ModestAccountViewWindowPrivate *priv = MODEST_ACCOUNT_VIEW_WINDOW_GET_PRIVATE(self); + + GtkWidget *main_vbox = gtk_vbox_new (FALSE, 6); + GtkWidget *main_hbox = gtk_hbox_new (FALSE, 6); + + priv->account_view = modest_account_view_new (modest_runtime_get_account_mgr()); + + /* Only force the height, the width of the widget will depend + on the size of the column titles */ + gtk_widget_set_size_request (GTK_WIDGET(priv->account_view), -1, 400); + gtk_widget_show (GTK_WIDGET (priv->account_view)); + + GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(priv->account_view)); + g_signal_connect (G_OBJECT(sel), "changed", G_CALLBACK(on_selection_changed), + self); + + GtkWidget *scrolled_window = gtk_scrolled_window_new (NULL, NULL); + gtk_container_set_border_width (GTK_CONTAINER (scrolled_window), MODEST_MARGIN_DEFAULT); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window), GTK_POLICY_NEVER, + GTK_POLICY_AUTOMATIC); + gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (priv->account_view)); + gtk_widget_show (GTK_WIDGET (scrolled_window)); + + gtk_box_pack_start (GTK_BOX(main_hbox), GTK_WIDGET(scrolled_window), TRUE, TRUE, 2); + + gtk_box_pack_start (GTK_BOX(main_vbox), main_hbox, TRUE, TRUE, 2); + gtk_widget_show (GTK_WIDGET (main_hbox)); + gtk_widget_show (GTK_WIDGET (main_vbox)); + + return main_vbox; +} + + +static void +modest_account_view_window_init (ModestAccountViewWindow *self) +{ + ModestAccountViewWindowPrivate *priv = MODEST_ACCOUNT_VIEW_WINDOW_GET_PRIVATE(self); + + priv->acc_removed_handler = 0; +} + +static void +on_account_removed (ModestAccountMgr *acc_mgr, + const gchar *account, + gpointer user_data) +{ + ModestAccountViewWindowPrivate *priv; + + /* If there is no account left then close the window */ + if (!modest_account_mgr_has_accounts (acc_mgr, TRUE)) { + gboolean ret_value; + g_signal_emit_by_name (G_OBJECT (user_data), "delete-event", NULL, &ret_value); + } else { + /* Re-focus the account list view widget */ + priv = MODEST_ACCOUNT_VIEW_WINDOW_GET_PRIVATE (user_data); + gtk_widget_grab_focus (GTK_WIDGET (priv->account_view)); + } +} + +GtkWidget* +modest_account_view_window_new (void) +{ + GObject *self = g_object_new(MODEST_TYPE_ACCOUNT_VIEW_WINDOW, NULL); + ModestAccountViewWindowPrivate *priv; + ModestAccountMgr *account_mgr = modest_runtime_get_account_mgr (); + + /* Add widgets */ + gtk_box_pack_start (GTK_BOX((GTK_DIALOG (self)->vbox)), + window_vbox_new (MODEST_ACCOUNT_VIEW_WINDOW (self)), + TRUE, TRUE, 2); + + setup_button_box (MODEST_ACCOUNT_VIEW_WINDOW (self), GTK_BUTTON_BOX (GTK_DIALOG (self)->action_area)); + + gtk_window_set_title (GTK_WINDOW (self), _("mcen_ti_emailsetup_accounts")); + + /* Connect signals */ + priv = MODEST_ACCOUNT_VIEW_WINDOW_GET_PRIVATE(self); + priv->acc_removed_handler = g_signal_connect (G_OBJECT(account_mgr), "account_removed", + G_CALLBACK (on_account_removed), self); + + return GTK_WIDGET (self); +} diff --git a/src/hildon2/modest-address-book.c b/src/hildon2/modest-address-book.c new file mode 100644 index 0000000..b636c6e --- /dev/null +++ b/src/hildon2/modest-address-book.c @@ -0,0 +1,996 @@ +/* Copyright (c) 2007, Nokia Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Nokia Corporation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* modest-address-book.c */ + +#include +#include +#include +#include +#include +#include +#include +#include "modest-hildon-includes.h" +#include "modest-platform.h" +#include "modest-runtime.h" +#include "widgets/modest-window-mgr.h" +#include +#include +#include +#include +#include +#include +#include + +static OssoABookContactModel *contact_model = NULL; +static EBook *book = NULL; +static EBookView * book_view = NULL; + +static GSList *get_recipients_for_given_contact(EContact * contact); +static void commit_contact(EContact * contact, gboolean is_new); +static gchar *get_email_addr_from_user(const gchar * given_name); +static gchar *ui_get_formatted_email_id(gchar * current_given_name, + gchar * current_sur_name, gchar * current_email_id); +static gchar *run_add_email_addr_to_contact_dlg(const gchar * contact_name); +static GSList *select_email_addrs_for_contact(GList * email_addr_list); +static gboolean resolve_address (const gchar *address, GSList **resolved_addresses, gchar **contact_id); +static gchar *unquote_string (const gchar *str); + + +static void +unref_gobject (GObject *obj) +{ + if (obj) + g_object_unref (obj); +} + + +static void +get_book_view_cb (EBook *book, EBookStatus status, EBookView *bookview, gpointer data) +{ + if (status != E_BOOK_ERROR_OK) { + g_object_unref (book); + book = NULL; + return; + } + book_view = bookview; + + if (contact_model) +#if MODEST_ABOOK_API < 4 + osso_abook_tree_model_set_book_view (OSSO_ABOOK_TREE_MODEL (contact_model), + book_view); +#else /* MODEST_ABOOK_API < 4 */ + osso_abook_list_store_set_book_view (OSSO_ABOOK_LIST_STORE (contact_model), + book_view); +#endif /* MODEST_ABOOK_API < 4 */ + + e_book_view_start (book_view); +} + +static void +book_open_cb (EBook *view, EBookStatus status, gpointer data) +{ + EBookQuery *query = NULL; + + if (status != E_BOOK_ERROR_OK) { + g_object_unref (book); + book = NULL; + return; + } + query = e_book_query_any_field_contains (""); + e_book_async_get_book_view (book, query, NULL, -1, get_book_view_cb, NULL); + e_book_query_unref (query); +} + +static gboolean +open_addressbook () +{ + book = e_book_new_system_addressbook (NULL); + if (!book) + return FALSE; + + e_book_async_open (book, FALSE, book_open_cb, NULL); + + return TRUE; /* FIXME */ +} + +static gboolean +open_addressbook_sync () +{ + book = e_book_new_system_addressbook (NULL); + if (!book) + return FALSE; + + e_book_open (book, FALSE, NULL); + + return TRUE; +} + +void +modest_address_book_add_address (const gchar *address) +{ + OssoABookAccount *account = NULL; + GtkWidget *dialog = NULL; + gchar *email_address = NULL; + + contact_model = osso_abook_contact_model_new (); + if (!open_addressbook ()) { + if (contact_model) { + g_object_unref (contact_model); + contact_model = NULL; + } + return; + } + + email_address = modest_text_utils_get_email_address (address); + + account = osso_abook_account_get (EVC_EMAIL, NULL, email_address); + g_free (email_address); + if (account) + { + dialog = osso_abook_add_to_contacts_dialog_new (contact_model, account); + g_object_unref (account); + gtk_dialog_run (GTK_DIALOG (dialog)); + + if (contact_model) { + g_object_unref (contact_model); + contact_model = NULL; + } + + gtk_widget_destroy (dialog); + } + +} + +void +modest_address_book_select_addresses (ModestRecptEditor *recpt_editor) +{ +#if MODEST_ABOOK_API < 4 + GtkWidget *contact_view = NULL; + GtkWidget *contact_dialog; +#else /* MODEST_ABOOK_API < 4 */ + OssoABookContactChooser *contact_chooser = NULL; +#endif /* MODEST_ABOOK_API < 4 */ + + GList *contacts_list = NULL; + GSList *email_addrs_per_contact = NULL; + gchar *econtact_id; + gboolean focus_recpt_editor = FALSE; + + g_return_if_fail (MODEST_IS_RECPT_EDITOR (recpt_editor)); + +#if MODEST_ABOOK_API < 4 + if (!open_addressbook ()) { + if (contact_model) { + g_object_unref (contact_model); + contact_model = NULL; + } + return; + } + contact_model = osso_abook_contact_model_new (); + + contact_view = osso_abook_contact_selector_new_basic (contact_model); + osso_abook_contact_selector_set_minimum_selection (OSSO_ABOOK_CONTACT_SELECTOR (contact_view), 1); + + contact_dialog = osso_abook_select_dialog_new (OSSO_ABOOK_TREE_VIEW (contact_view)); + gtk_window_set_title (GTK_WINDOW (contact_dialog), _("mcen_ti_select_recipients")); + modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), GTK_WINDOW (contact_dialog)); + + gtk_widget_show (contact_dialog); + + if (gtk_dialog_run (GTK_DIALOG (contact_dialog)) == GTK_RESPONSE_OK) { + contacts_list = + osso_abook_contact_selector_get_extended_selection (OSSO_ABOOK_CONTACT_SELECTOR + (contact_view)); + } +#else /* MODEST_ABOOK_API < 4 */ + /* TODO: figure out how to make the contact chooser modal */ + contact_chooser = osso_abook_contact_chooser_new + ("title", _("mcen_ti_select_recipients"), + "help-topic", "", + "minimum-selection", 1, NULL); + + if (osso_abook_contact_chooser_run (contact_chooser) == GTK_RESPONSE_OK) + contacts_list = osso_abook_contact_chooser_get_selection (contact_chooser); + + g_object_unref (contact_chooser); +#endif + + if (contacts_list) { + GList *node; + + for (node = contacts_list; node != NULL; node = g_list_next (node)) { + EContact *contact = (EContact *) node->data; + + email_addrs_per_contact = get_recipients_for_given_contact (contact); + if (email_addrs_per_contact) { + econtact_id = (gchar *) e_contact_get_const (contact, E_CONTACT_UID); + modest_recpt_editor_add_resolved_recipient (MODEST_RECPT_EDITOR (recpt_editor), + email_addrs_per_contact, econtact_id); + g_slist_foreach (email_addrs_per_contact, (GFunc) g_free, NULL); + g_slist_free (email_addrs_per_contact); + email_addrs_per_contact = NULL; + focus_recpt_editor = TRUE; + } + } + g_list_free (contacts_list); + } + +#if MODEST_ABOOK_API < 4 + if (contact_model) { + g_object_unref (contact_model); + contact_model = NULL; + } + + gtk_widget_destroy (contact_dialog); +#endif + + if (focus_recpt_editor) + modest_recpt_editor_grab_focus (MODEST_RECPT_EDITOR (recpt_editor)); + +} + +/** + * This function returns the resolved recipients for a given EContact. + * If no e-mail address is defined, it launches 'Add e-mail address to contact' + * dialog to obtain one. If multiple e-mail addresses are found, it launches + * 'Select e-mail address' dialog to allow user to select one or more e-mail + * addresses for that contact. + * + * @param Contact of type #EContact + * @return List of resolved recipient strings, to be freed by calling function. + */ +static GSList *get_recipients_for_given_contact(EContact * contact) +{ + gchar *givenname = NULL; + gchar *familyname = NULL; + gchar *nickname = NULL; + gchar *emailid = NULL; + const gchar *display_name = NULL; + GList *list = NULL; + gchar *formatted_string = NULL; + gboolean email_not_present = FALSE; + GSList *formattedlist = NULL, *selected_email_addr_list = NULL, *node = NULL; + + if (!contact) { + return NULL; + } + + givenname = (gchar *) e_contact_get_const(contact, E_CONTACT_GIVEN_NAME); + familyname = (gchar *) e_contact_get_const(contact, E_CONTACT_FAMILY_NAME); + nickname = (gchar *) e_contact_get_const(contact, E_CONTACT_NICKNAME); + if (!nickname) + nickname = ""; + list = (GList *) e_contact_get(contact, E_CONTACT_EMAIL); + + if (!list) { + email_not_present = TRUE; + } + + if (list && g_list_length(list) == 1) { + if (list->data == NULL || g_utf8_strlen(list->data, -1) == 0) { + email_not_present = TRUE; + } else { + emailid = g_strstrip(g_strdup(list->data)); + if (g_utf8_strlen(emailid, -1) == 0) { + g_free(emailid); + email_not_present = TRUE; + } + } + } + + /*Launch the 'Add e-mail addr to contact' dialog if required */ + if (email_not_present) { +#if MODEST_ABOOK_API < 4 + display_name = osso_abook_contact_get_display_name(contact); +#else + OssoABookContact *abook_contact; + + abook_contact = osso_abook_contact_new_from_template (contact); + display_name = osso_abook_contact_get_display_name(abook_contact); + g_object_unref (abook_contact); +#endif + + emailid = get_email_addr_from_user(display_name); + if (emailid) { + e_contact_set(contact, E_CONTACT_EMAIL_1, emailid); + commit_contact(contact, FALSE); + } + } + + if (emailid) { + if (givenname || familyname) + formatted_string = + ui_get_formatted_email_id(givenname, familyname, emailid); + else + formatted_string = g_strdup(emailid); + formattedlist = g_slist_append(formattedlist, formatted_string); + g_free(emailid); + } + + /*Launch the 'Select e-mail address' dialog if required */ + if (g_list_length(list) > 1) { + selected_email_addr_list = select_email_addrs_for_contact(list); + for (node = selected_email_addr_list; node != NULL; node = node->next) { + if (givenname || familyname) + formatted_string = + ui_get_formatted_email_id(givenname, familyname, node->data); + else + formatted_string = g_strdup(node->data); + formattedlist = g_slist_append(formattedlist, formatted_string); + } + if (selected_email_addr_list) { + g_slist_foreach(selected_email_addr_list, (GFunc) g_free, NULL); + g_slist_free(selected_email_addr_list); + } + } + + if (list) { + g_list_foreach(list, (GFunc) g_free, NULL); + g_list_free(list); + } + + return formattedlist; +} + +/** + * This is a helper function to commit a EContact to Address_Book application. + * + * @param contact Contact of type #EContact + * @return void + */ +static void +commit_contact(EContact * contact, gboolean is_new) +{ + g_return_if_fail (contact); + g_return_if_fail (book); + + if (!contact || !book) + return; + +#if MODEST_ABOOK_API < 2 + osso_abook_contact_commit(contact, is_new, book); +#else + osso_abook_contact_commit(contact, is_new, book, NULL); +#endif /* MODEST_ABOOK_API < 2 */ +} + +/** + * This is a helper function used to launch 'Add e-mail address to contact' dialog + * after showing appropriate notification, when there is no e-mail address defined + * for a selected contact. + * + * @param given_name Given name of the contact + * @param family_name Family name of the contact + * @return E-mail address string entered by user, to be freed by calling function. + */ +static gchar * +get_email_addr_from_user(const gchar * given_name) +{ + gchar *notification = NULL; + gchar *email_addr = NULL; + GtkWidget *note; + gboolean note_response; + + + notification = g_strdup_printf(_("mcen_nc_email_address_not_defined"), given_name); + + note = hildon_note_new_confirmation (NULL, notification); + note_response = gtk_dialog_run (GTK_DIALOG(note)); + gtk_widget_destroy (note); + g_free(notification); + + if (note_response == GTK_RESPONSE_OK) { + email_addr = run_add_email_addr_to_contact_dlg(given_name); + } + + return email_addr; +} + +/** +This function is used to get the formated email id with given name and sur name +in the format "GIVENNAME SURNAME ". +@param current_given_name to hold the given name +@param current_sur_name to hold the sur name +@param current_email_id to hold the email id. +@return gchar* string to be freed by calling function +*/ +static gchar * +ui_get_formatted_email_id(gchar * current_given_name, + gchar * current_sur_name, gchar * current_email_id) +{ + GString *email_id_str = NULL; + + email_id_str = g_string_new(NULL); + + if ((current_given_name != NULL) && ((strlen(current_given_name) != 0)) + && (current_sur_name != NULL) && ((strlen(current_sur_name) != 0))) { + g_string_append_printf(email_id_str, "%s %s", current_given_name, current_sur_name); + } else if ((current_given_name != NULL) && (strlen(current_given_name) != 0)) { + g_string_append_printf(email_id_str, "%s", current_given_name); + } else if ((current_sur_name != NULL) && (strlen(current_sur_name) != 0)) { + g_string_append_printf(email_id_str, "%s", current_sur_name); + } + if (g_utf8_strchr (email_id_str->str, -1, ' ')) { + g_string_prepend_c (email_id_str, '\"'); + g_string_append_c (email_id_str, '\"'); + } + g_string_append_printf (email_id_str, " %c%s%c", '<', current_email_id, '>'); + return g_string_free (email_id_str, FALSE); +} + +/** + * This is a helper function used to create & run 'Add e-mail address to contact' dialog. + * It allows user to enter an e-mail address, and shows appropriate infonote if the + * entered string is not a valid e-mail address. + * + * @param contact_name Full name of the contact + * @return E-mail address string entered by user, to be freed by calling function. + */ +static gchar * +run_add_email_addr_to_contact_dlg(const gchar * contact_name) +{ + GtkWidget *add_email_addr_to_contact_dlg = NULL; + GtkSizeGroup *size_group = NULL; + GtkWidget *cptn_cntrl = NULL; + GtkWidget *name_label = NULL; + GtkWidget *email_entry = NULL; + gint result = -1; + gchar *new_email_addr = NULL; + gboolean run_dialog = TRUE; + + add_email_addr_to_contact_dlg = + gtk_dialog_new_with_buttons(_("mcen_ti_add_email_title"), NULL, + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + _("mcen_bd_dialog_ok"), GTK_RESPONSE_ACCEPT, + _("mcen_bd_dialog_cancel"), GTK_RESPONSE_REJECT, NULL); + gtk_dialog_set_has_separator(GTK_DIALOG(add_email_addr_to_contact_dlg), FALSE); + /*Set app_name & state_save related tags to the window */ + + size_group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); + name_label = gtk_label_new(contact_name); + gtk_misc_set_alignment(GTK_MISC(name_label), 0, 0); + cptn_cntrl = + hildon_caption_new(size_group, _("mcen_ia_add_email_name"), name_label, NULL, + HILDON_CAPTION_OPTIONAL); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(add_email_addr_to_contact_dlg)->vbox), cptn_cntrl, + FALSE, FALSE, 0); + + email_entry = gtk_entry_new(); + cptn_cntrl = + hildon_caption_new(size_group, _("mcen_fi_add_email_name"), email_entry, NULL, + HILDON_CAPTION_OPTIONAL); + hildon_gtk_entry_set_input_mode(GTK_ENTRY(email_entry), HILDON_GTK_INPUT_MODE_FULL); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(add_email_addr_to_contact_dlg)->vbox), cptn_cntrl, + TRUE, TRUE, 0); + + gtk_widget_show_all(add_email_addr_to_contact_dlg); + + while (run_dialog) { + run_dialog = FALSE; + gtk_widget_grab_focus(email_entry); + result = gtk_dialog_run(GTK_DIALOG(add_email_addr_to_contact_dlg)); + + if (result == GTK_RESPONSE_ACCEPT) { + const gchar *invalid_char_offset = NULL; + new_email_addr = g_strdup(gtk_entry_get_text(GTK_ENTRY(email_entry))); + new_email_addr = g_strstrip(new_email_addr); + if (!modest_text_utils_validate_email_address (new_email_addr, &invalid_char_offset)) { + gtk_widget_grab_focus(email_entry); + if ((invalid_char_offset != NULL)&&(*invalid_char_offset != '\0')) { + gchar *char_in_string = g_strdup_printf ("%c", *invalid_char_offset); + gchar *message = g_strdup_printf( + dgettext("hildon-common-strings", "ckdg_ib_illegal_characters_entered"), + char_in_string); + hildon_banner_show_information ( + add_email_addr_to_contact_dlg, NULL, message ); + g_free (message); + } else { + hildon_banner_show_information (add_email_addr_to_contact_dlg, NULL, _("mcen_ib_invalid_email")); + run_dialog = TRUE; + } + gtk_editable_select_region((GtkEditable *) email_entry, 0, -1); + g_free(new_email_addr); + new_email_addr = NULL; + } + } + } + + gtk_widget_destroy(add_email_addr_to_contact_dlg); + + return new_email_addr; +} + +/** + * This is helper function to create & run 'Select e-mail address' dialog, used when + * multiple e-mail addresses are found for a selected contact. It allows user to select + * one or more e-mail addresses for that contact. + * + * @param email_addr_list List of e-mail addresses for that contact + * @return List of user selected e-mail addresses, to be freed by calling function. + */ +static GSList * +select_email_addrs_for_contact(GList * email_addr_list) +{ + GtkWidget *select_email_addr_dlg = NULL; + GtkWidget *view = NULL, *scrolledwindow = NULL; + GtkTreeSelection *selection = NULL; + GtkCellRenderer *renderer = NULL; + GtkTreeViewColumn *col = NULL; + GtkListStore *list_store = NULL; + GtkTreeModel *model = NULL; + GtkTreeIter iter; + GList *pathslist = NULL, *node = NULL; + gint result = -1; + gchar *email_addr = NULL; + GSList *selected_email_addr_list = NULL; + + if (!email_addr_list) + return NULL; + + select_email_addr_dlg = + gtk_dialog_new_with_buttons(_("mcen_ti_select_email_title"), + NULL, + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + _("mcen_bd_dialog_ok"), GTK_RESPONSE_ACCEPT, + _("mcen_bd_dialog_cancel"), GTK_RESPONSE_REJECT, NULL); + gtk_dialog_set_has_separator(GTK_DIALOG(select_email_addr_dlg), FALSE); + + /* Make the window approximately big enough, because it doesn't resize to be big enough + * for the window title text: */ + gtk_window_set_default_size (GTK_WINDOW (select_email_addr_dlg), 400, -1); + + scrolledwindow = gtk_scrolled_window_new(NULL, NULL); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(select_email_addr_dlg)->vbox), scrolledwindow, TRUE, + TRUE, 0); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwindow), GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + + view = gtk_tree_view_new(); + col = gtk_tree_view_column_new(); + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_append_column(GTK_TREE_VIEW(view), col); + gtk_tree_view_column_pack_start(col, renderer, TRUE); + gtk_tree_view_column_add_attribute(col, renderer, "text", 0); + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view)); + gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE); + gtk_container_add(GTK_CONTAINER(scrolledwindow), view); + + list_store = gtk_list_store_new(1, G_TYPE_STRING); + model = GTK_TREE_MODEL(list_store); + gtk_tree_view_set_model(GTK_TREE_VIEW(view), model); + + for (node = email_addr_list; node != NULL && node->data != NULL; node = node->next) { + email_addr = g_strstrip(g_strdup(node->data)); + gtk_list_store_append(list_store, &iter); + gtk_list_store_set(list_store, &iter, 0, email_addr, -1); + g_free(email_addr); + } + gtk_tree_model_get_iter_first (GTK_TREE_MODEL (list_store), &iter); + gtk_tree_selection_select_iter (selection, &iter); + + gtk_widget_show_all(select_email_addr_dlg); + result = gtk_dialog_run(GTK_DIALOG(select_email_addr_dlg)); + + if (result == GTK_RESPONSE_ACCEPT) { + pathslist = gtk_tree_selection_get_selected_rows(selection, NULL); + for (node = pathslist; node != NULL; node = node->next) { + if (gtk_tree_model_get_iter(model, &iter, (GtkTreePath *) node->data)) { + gtk_tree_model_get(model, &iter, 0, &email_addr, -1); + selected_email_addr_list = + g_slist_append(selected_email_addr_list, g_strdup(email_addr)); + g_free(email_addr); + } + } + } + + gtk_list_store_clear(list_store); + gtk_widget_destroy(select_email_addr_dlg); + return selected_email_addr_list; +} + + +static gboolean /* make this public? */ +add_to_address_book (const gchar* address) +{ + EBookQuery *query; + GList *contacts = NULL; + GError *err = NULL; + gchar *email; + + g_return_val_if_fail (address, FALSE); + + if (!book) + open_addressbook (); + + g_return_val_if_fail (book, FALSE); + + email = modest_text_utils_get_email_address (address); + + query = e_book_query_field_test (E_CONTACT_EMAIL, E_BOOK_QUERY_IS, email); + if (!e_book_get_contacts (book, query, &contacts, &err)) { + g_printerr ("modest: failed to get contacts: %s", + err ? err->message : ""); + if (err) + g_error_free (err); + return FALSE; + } + e_book_query_unref (query); + + /* we need to 'commit' it, even if we already found the email + * address in the addressbook; thus, it will show up in the 'recent list' */ + if (contacts) { + g_debug ("%s already in the address book", address); + commit_contact ((EContact*)contacts->data, FALSE); + + g_list_foreach (contacts, (GFunc)unref_gobject, NULL); + g_list_free (contacts); + + } else { + /* it's not yet in the addressbook, add it now! */ + EContact *new_contact = e_contact_new (); + gchar *display_address; + display_address = g_strdup (address); + if (display_address) { + modest_text_utils_get_display_address (display_address); + if ((display_address[0] != '\0') && (strlen (display_address) != strlen (address))) + e_contact_set (new_contact, E_CONTACT_FULL_NAME, (const gpointer)display_address); + } + e_contact_set (new_contact, E_CONTACT_EMAIL_1, (const gpointer)email); + g_free (display_address); + commit_contact (new_contact, TRUE); + g_debug ("%s added to address book", address); + g_object_unref (new_contact); + } + + g_free (email); + + return TRUE; +} + +static gboolean +show_check_names_banner (gpointer userdata) +{ + GtkWidget **banner = (GtkWidget **) userdata; + + gdk_threads_enter (); + *banner = modest_platform_animation_banner (NULL, NULL, _("mail_ib_checking_names")); + gdk_threads_leave (); + + return FALSE; +} + +static void +hide_check_names_banner (GtkWidget **banner, guint banner_timeout) +{ + if (*banner != NULL) { + gtk_widget_destroy (*banner); + *banner = NULL; + } else { + g_source_remove (banner_timeout); + } + +} + +gboolean +modest_address_book_check_names (ModestRecptEditor *recpt_editor, gboolean update_addressbook) +{ + const gchar *recipients = NULL; + GSList *start_indexes = NULL, *end_indexes = NULL; + GSList *current_start, *current_end; + gboolean result = TRUE; + GtkTextBuffer *buffer; + gint offset_delta = 0; + gint last_length; + GtkTextIter start_iter, end_iter; + + g_return_val_if_fail (MODEST_IS_RECPT_EDITOR (recpt_editor), FALSE); + + recipients = modest_recpt_editor_get_recipients (recpt_editor); + last_length = g_utf8_strlen (recipients, -1); + modest_text_utils_get_addresses_indexes (recipients, &start_indexes, &end_indexes); + + if (start_indexes == NULL) { + if (last_length != 0) { + hildon_banner_show_information (NULL, NULL, _("mcen_nc_no_matching_contacts")); + return FALSE; + } else { + return TRUE; + } + } + + current_start = start_indexes; + current_end = end_indexes; + buffer = modest_recpt_editor_get_buffer (recpt_editor); + + while (current_start != NULL) { + gchar *address; + gchar *start_ptr, *end_ptr; + gint start_pos, end_pos; + const gchar *invalid_char_position = NULL; + gboolean store_address = FALSE; + + start_pos = (*((gint*) current_start->data)) + offset_delta; + end_pos = (*((gint*) current_end->data)) + offset_delta; + + start_ptr = g_utf8_offset_to_pointer (recipients, start_pos); + end_ptr = g_utf8_offset_to_pointer (recipients, end_pos); + + address = g_strstrip (g_strndup (start_ptr, end_ptr - start_ptr)); + gtk_text_buffer_get_iter_at_offset (buffer, &start_iter, start_pos); + gtk_text_buffer_get_iter_at_offset (buffer, &end_iter, end_pos); + gtk_text_buffer_select_range (buffer, &start_iter, &end_iter); + + if (!modest_text_utils_validate_recipient (address, &invalid_char_position)) { + if ((invalid_char_position != NULL) && (*invalid_char_position != '\0')) { + gchar *char_in_string = g_strdup_printf("%c", *invalid_char_position); + gchar *message = g_strdup_printf( + dgettext("hildon-common-strings", "ckdg_ib_illegal_characters_entered"), + char_in_string); + g_free (char_in_string); + hildon_banner_show_information (NULL, NULL, message ); + g_free (message); + result = FALSE; + } else if (strstr (address, "@") == NULL) { + /* here goes searching in addressbook */ + gchar *contact_id = NULL; + GSList *resolved_addresses = NULL; + + result = resolve_address (address, &resolved_addresses, &contact_id); + + if (result) { + gint new_length; + /* replace string */ + modest_recpt_editor_replace_with_resolved_recipient (recpt_editor, + &start_iter, &end_iter, + resolved_addresses, + contact_id); + g_free (contact_id); + g_slist_foreach (resolved_addresses, (GFunc) g_free, NULL); + g_slist_free (resolved_addresses); + + /* update offset delta */ + recipients = modest_recpt_editor_get_recipients (recpt_editor); + new_length = g_utf8_strlen (recipients, -1); + offset_delta = offset_delta + new_length - last_length; + last_length = new_length; + } + } else { + /* this address is not valid, select it and return control to user showing banner */ + hildon_banner_show_information (NULL, NULL, _("mcen_ib_invalid_email")); + result = FALSE; + } + } else { + GSList *tags, *node; + gboolean has_recipient = FALSE; + + tags = gtk_text_iter_get_tags (&start_iter); + for (node = tags; node != NULL; node = g_slist_next (node)) { + GtkTextTag *tag = GTK_TEXT_TAG (node->data); + if (g_object_get_data (G_OBJECT (tag), "recipient-tag-id") != NULL) { + has_recipient = TRUE; + break; + } + } + g_slist_free (tags); + if (!has_recipient) { + GSList * address_list = NULL; + + address_list = g_slist_prepend (address_list, address); + modest_recpt_editor_replace_with_resolved_recipient (recpt_editor, + &start_iter, &end_iter, + address_list, + ""); + g_slist_free (address_list); + store_address = TRUE; + } + } + + /* so, it seems a valid address */ + /* note: adding it the to the addressbook if it did not exist yet, + * and adding it to the recent_list */ + if (result && update_addressbook && store_address) + add_to_address_book (address); + + g_free (address); + if (result == FALSE) + break; + + current_start = g_slist_next (current_start); + current_end = g_slist_next (current_end); + } + + if (current_start == NULL) { + gtk_text_buffer_get_end_iter (buffer, &end_iter); + gtk_text_buffer_place_cursor (buffer, &end_iter); + } + + g_slist_foreach (start_indexes, (GFunc) g_free, NULL); + g_slist_foreach (end_indexes, (GFunc) g_free, NULL); + g_slist_free (start_indexes); + g_slist_free (end_indexes); + + return result; + +} + +static GList * +get_contacts_for_name (const gchar *name) +{ + EBookQuery *full_name_book_query = NULL; + GList *result; + gchar *unquoted; + + if (name == NULL) + return NULL; + + unquoted = unquote_string (name); + full_name_book_query = e_book_query_field_test (E_CONTACT_FULL_NAME, E_BOOK_QUERY_CONTAINS, unquoted); + g_free (unquoted); + + e_book_get_contacts (book, full_name_book_query, &result, NULL); + e_book_query_unref (full_name_book_query); + + return result; +} + +static GList * +select_contacts_for_name_dialog (const gchar *name) +{ + EBookQuery *full_name_book_query = NULL; + EBookView *book_view = NULL; + GList *result = NULL; + gchar *unquoted; + + unquoted = unquote_string (name); + full_name_book_query = e_book_query_field_test (E_CONTACT_FULL_NAME, E_BOOK_QUERY_CONTAINS, unquoted); + g_free (unquoted); + e_book_get_book_view (book, full_name_book_query, NULL, -1, &book_view, NULL); + e_book_query_unref (full_name_book_query); + + if (book_view) { + GtkWidget *contact_view = NULL; + GtkWidget *contact_dialog = NULL; +#if MODEST_ABOOK_API < 4 + osso_abook_tree_model_set_book_view (OSSO_ABOOK_TREE_MODEL (contact_model), book_view); +#else /* MODEST_ABOOK_API < 4 */ + osso_abook_list_store_set_book_view (OSSO_ABOOK_LIST_STORE (contact_model), book_view); +#endif /* MODEST_ABOOK_API < 4 */ + e_book_view_start (book_view); + + contact_view = osso_abook_contact_selector_new_basic (contact_model); + contact_dialog = osso_abook_select_dialog_new (OSSO_ABOOK_TREE_VIEW (contact_view)); + gtk_window_set_title (GTK_WINDOW (contact_dialog), _("mcen_ti_select_recipients")); + + if (gtk_dialog_run (GTK_DIALOG (contact_dialog)) == GTK_RESPONSE_OK) { + result = osso_abook_contact_view_get_selection (OSSO_ABOOK_CONTACT_VIEW (contact_view)); + } + e_book_view_stop (book_view); + g_object_unref (book_view); + gtk_widget_destroy (contact_dialog); + } + + return result; +} + +static gboolean +resolve_address (const gchar *address, GSList **resolved_addresses, gchar **contact_id) +{ + GList *resolved_contacts; + guint banner_timeout; + GtkWidget *banner = NULL; + + banner_timeout = g_timeout_add (500, show_check_names_banner, &banner); + + contact_model = osso_abook_contact_model_new (); + if (!open_addressbook_sync ()) { + if (contact_model) { + g_object_unref (contact_model); + contact_model = NULL; + } + return FALSE; + } + + resolved_contacts = get_contacts_for_name (address); + + if (resolved_contacts == NULL) { + /* no matching contacts for the search string */ + modest_platform_run_information_dialog (NULL, _("mcen_nc_no_matching_contacts"), FALSE); + hide_check_names_banner (&banner, banner_timeout); + + return FALSE; + } + + if (g_list_length (resolved_contacts) > 1) { + /* show a dialog to select the contact from the resolved ones */ + g_list_free (resolved_contacts); + + hide_check_names_banner (&banner, banner_timeout); + resolved_contacts = select_contacts_for_name_dialog (address); + banner_timeout = g_timeout_add (500, show_check_names_banner, &banner); + + } + + /* get the resolved contacts (can be no contact) */ + if (resolved_contacts) { + gboolean found; + EContact *contact = (EContact *) resolved_contacts->data; + + *resolved_addresses = get_recipients_for_given_contact (contact); + hide_check_names_banner (&banner, banner_timeout); + if (*resolved_addresses) { + *contact_id = g_strdup (e_contact_get_const (contact, E_CONTACT_UID)); + found = TRUE; + } else { + found = FALSE; + } + + g_list_foreach (resolved_contacts, (GFunc)unref_gobject, NULL); + g_list_free (resolved_contacts); + + return found; + } else { + /* cancelled dialog to select more than one contact or + * selected no contact */ + hide_check_names_banner (&banner, banner_timeout); + return FALSE; + } + +} + +static gchar * +unquote_string (const gchar *str) +{ + GString *buffer; + gchar *p; + + if (str == NULL) + return NULL; + + buffer = g_string_new_len (NULL, strlen (str)); + for (p = (gchar *) str; *p != '\0'; p = g_utf8_next_char (p)) { + if (*p == '"') { + p = g_utf8_next_char (p); + while ((*p != '\0')&&(*p != '"')) { + if (*p == '\\') { + g_string_append_unichar (buffer, g_utf8_get_char (p)); + p = g_utf8_next_char (p); + + } + g_string_append_unichar (buffer, g_utf8_get_char (p)); + p = g_utf8_next_char (p); + } + } else { + g_string_append_unichar (buffer, g_utf8_get_char (p)); + } + } + + return g_string_free (buffer, FALSE); + +} diff --git a/src/hildon2/modest-connection-specific-smtp-edit-window.c b/src/hildon2/modest-connection-specific-smtp-edit-window.c new file mode 100644 index 0000000..37886bc --- /dev/null +++ b/src/hildon2/modest-connection-specific-smtp-edit-window.c @@ -0,0 +1,525 @@ +/* Copyright (c) 2006, Nokia Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Nokia Corporation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "modest-connection-specific-smtp-edit-window.h" +#include "widgets/modest-ui-constants.h" +#include "modest-hildon-includes.h" +#include "modest-runtime.h" + +#include "widgets/modest-serversecurity-combo-box.h" +#include "widgets/modest-secureauth-combo-box.h" +#include "widgets/modest-validating-entry.h" +#include +#include +#include +#include +#include "modest-text-utils.h" +#include "modest-maemo-utils.h" + +#include + +#define PORT_RANGE_MIN 1 +#define PORT_RANGE_MAX 65535 + +G_DEFINE_TYPE (ModestConnectionSpecificSmtpEditWindow, modest_connection_specific_smtp_edit_window, GTK_TYPE_DIALOG); + +#define CONNECTION_SPECIFIC_SMTP_EDIT_WINDOW_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), MODEST_TYPE_CONNECTION_SPECIFIC_SMTP_EDIT_WINDOW, ModestConnectionSpecificSmtpEditWindowPrivate)) + +static void on_response (GtkDialog *dialog, + gint arg1, + gpointer user_data); + +typedef struct _ModestConnectionSpecificSmtpEditWindowPrivate ModestConnectionSpecificSmtpEditWindowPrivate; + +struct _ModestConnectionSpecificSmtpEditWindowPrivate +{ + GtkWidget *entry_outgoingserver; + GtkWidget *combo_outgoing_auth; + GtkWidget *entry_user_username; + GtkWidget *entry_user_password; + GtkWidget *combo_outgoing_security; + GtkWidget *entry_port; + + GtkWidget *button_ok; + GtkWidget *button_cancel; + + gchar *account_name; + + gboolean is_dirty; + gboolean range_error_occured; + guint range_error_banner_timeout; +}; + +static void +modest_connection_specific_smtp_edit_window_get_property (GObject *object, guint property_id, + GValue *value, GParamSpec *pspec) +{ + switch (property_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +modest_connection_specific_smtp_edit_window_set_property (GObject *object, guint property_id, + const GValue *value, GParamSpec *pspec) +{ + switch (property_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +modest_connection_specific_smtp_edit_window_dispose (GObject *object) +{ + + + if (G_OBJECT_CLASS (modest_connection_specific_smtp_edit_window_parent_class)->dispose) + G_OBJECT_CLASS (modest_connection_specific_smtp_edit_window_parent_class)->dispose (object); +} + +static void +modest_connection_specific_smtp_edit_window_finalize (GObject *object) +{ + ModestConnectionSpecificSmtpEditWindow *self = (ModestConnectionSpecificSmtpEditWindow *) object; + ModestConnectionSpecificSmtpEditWindowPrivate *priv = + CONNECTION_SPECIFIC_SMTP_EDIT_WINDOW_GET_PRIVATE (self); + + if (priv->range_error_banner_timeout > 0) { + g_source_remove (priv->range_error_banner_timeout); + priv->range_error_banner_timeout = 0; + } + if (priv->account_name) { + g_free (priv->account_name); + priv->account_name = NULL; + } + G_OBJECT_CLASS (modest_connection_specific_smtp_edit_window_parent_class)->finalize (object); +} + +static void +modest_connection_specific_smtp_edit_window_class_init (ModestConnectionSpecificSmtpEditWindowClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (ModestConnectionSpecificSmtpEditWindowPrivate)); + + object_class->get_property = modest_connection_specific_smtp_edit_window_get_property; + object_class->set_property = modest_connection_specific_smtp_edit_window_set_property; + object_class->dispose = modest_connection_specific_smtp_edit_window_dispose; + object_class->finalize = modest_connection_specific_smtp_edit_window_finalize; +} + +enum MODEL_COLS { + MODEL_COL_NAME = 0, + MODEL_COL_SERVER_NAME = 1, + MODEL_COL_ID = 2 +}; + +static void +on_change(GtkWidget* widget, ModestConnectionSpecificSmtpEditWindow *self) +{ + ModestConnectionSpecificSmtpEditWindowPrivate *priv = + CONNECTION_SPECIFIC_SMTP_EDIT_WINDOW_GET_PRIVATE (self); + priv->is_dirty = TRUE; +} + +static void +on_value_changed(GtkWidget* widget, GValue* value, ModestConnectionSpecificSmtpEditWindow *self) +{ + ModestConnectionSpecificSmtpEditWindowPrivate *priv = + CONNECTION_SPECIFIC_SMTP_EDIT_WINDOW_GET_PRIVATE (self); + + priv->range_error_occured = FALSE; + on_change(widget, self); +} + +gboolean +show_banner_handler (gpointer userdata) +{ + ModestConnectionSpecificSmtpEditWindow *self = userdata; + ModestConnectionSpecificSmtpEditWindowPrivate *priv = + CONNECTION_SPECIFIC_SMTP_EDIT_WINDOW_GET_PRIVATE (self); + gchar *msg; + + msg = g_strdup_printf (dgettext("hildon-libs", "ckct_ib_set_a_value_within_range"), PORT_RANGE_MIN, PORT_RANGE_MAX); + + hildon_banner_show_information (NULL, NULL, msg); + g_free (msg); + + priv->range_error_banner_timeout = 0; + return FALSE; +} + +static gboolean +on_range_error (GtkWidget *widget, HildonNumberEditorErrorType type, gpointer user_data) +{ + ModestConnectionSpecificSmtpEditWindow *self = user_data; + ModestConnectionSpecificSmtpEditWindowPrivate *priv = + CONNECTION_SPECIFIC_SMTP_EDIT_WINDOW_GET_PRIVATE (self); + + /* We want to prevent the closure of the dialog when a range error occured. The problem is that + * the hildon number editor already resets the value to the default value, so we have to + * remember that such an error occured. */ + priv->range_error_occured = TRUE; + if (priv->range_error_banner_timeout == 0) + priv->range_error_banner_timeout = g_timeout_add (200, show_banner_handler, self); + + /* Show error message by not returning TRUE */ + return TRUE; +} + +static void +on_response (GtkDialog *dialog, int response_id, gpointer user_data) +{ + const gchar *hostname; + ModestConnectionSpecificSmtpEditWindow *self = user_data; + ModestConnectionSpecificSmtpEditWindowPrivate *priv = + CONNECTION_SPECIFIC_SMTP_EDIT_WINDOW_GET_PRIVATE (self); + + hostname = gtk_entry_get_text (GTK_ENTRY (priv->entry_outgoingserver)); + + if ((response_id == GTK_RESPONSE_CANCEL) && + (priv->range_error_banner_timeout > 0)) { + g_source_remove (priv->range_error_banner_timeout); + priv->range_error_banner_timeout = 0; + } + + /* Don't close the dialog if a range error occured */ + if(response_id == GTK_RESPONSE_OK && priv->range_error_occured) + { + priv->range_error_occured = FALSE; + g_signal_stop_emission_by_name (dialog, "response"); + gtk_widget_grab_focus (priv->entry_port); + return; + } + + /* Don't close the dialog if a range error occured */ + if(response_id == GTK_RESPONSE_OK) { + if (hostname && (hostname[0] != '\0') && + (!modest_text_utils_validate_domain_name (hostname))) { + g_signal_stop_emission_by_name (dialog, "response"); + hildon_banner_show_information (NULL, NULL, _("mcen_ib_invalid_servername")); + gtk_widget_grab_focus (priv->entry_outgoingserver); + gtk_editable_select_region (GTK_EDITABLE (priv->entry_outgoingserver), 0, -1); + return; + } + } else { + /* Ask user if they want to discard changes */ + if (priv->is_dirty) { + gint response; + response = modest_platform_run_confirmation_dialog (GTK_WINDOW (user_data), + _("imum_nc_wizard_confirm_lose_changes")); + if (response == GTK_RESPONSE_CANCEL) + g_signal_stop_emission_by_name (dialog, "response"); + } + } +} + +static void on_set_focus_child (GtkContainer *container, GtkWidget *widget, gpointer user_data) +{ + ModestConnectionSpecificSmtpEditWindow *self = user_data; + ModestConnectionSpecificSmtpEditWindowPrivate *priv = + CONNECTION_SPECIFIC_SMTP_EDIT_WINDOW_GET_PRIVATE (self); + + /* Another child gained focus. Since the number editor already reset a + * possible range error to the default value, we allow closure of the + * dialog */ + priv->range_error_occured = FALSE; +} + +static void +on_combo_security_changed (GtkComboBox *widget, gpointer user_data) +{ + ModestConnectionSpecificSmtpEditWindow *self = + MODEST_CONNECTION_SPECIFIC_SMTP_EDIT_WINDOW (user_data); + ModestConnectionSpecificSmtpEditWindowPrivate *priv = + CONNECTION_SPECIFIC_SMTP_EDIT_WINDOW_GET_PRIVATE (self); + + on_change(GTK_WIDGET(widget), self); + + const gint port_number = + modest_serversecurity_combo_box_get_active_serversecurity_port ( + MODEST_SERVERSECURITY_COMBO_BOX (priv->combo_outgoing_security)); + + if(port_number != 0) { + hildon_number_editor_set_value ( + HILDON_NUMBER_EDITOR (priv->entry_port), port_number); + } +} + +static void +modest_connection_specific_smtp_edit_window_init (ModestConnectionSpecificSmtpEditWindow *self) +{ + ModestConnectionSpecificSmtpEditWindowPrivate *priv; + GtkWidget *dialog_box; + GtkWidget *scrolled_window, *vbox; + + /* The title of this dialog is quite long, so make the window wide enough */ + gtk_widget_set_size_request (GTK_WIDGET (self), 600, -1); + + priv = CONNECTION_SPECIFIC_SMTP_EDIT_WINDOW_GET_PRIVATE (self); + dialog_box = GTK_DIALOG(self)->vbox; /* gtk_vbox_new (FALSE, MODEST_MARGIN_HALF); */ + gtk_box_set_spacing (GTK_BOX (dialog_box), MODEST_MARGIN_NONE); + gtk_container_set_border_width (GTK_CONTAINER (dialog_box), MODEST_MARGIN_HALF); + + vbox = gtk_vbox_new (FALSE, 0); + + /* Create a size group to be used by all captions. + * Note that HildonCaption does not create a default size group if we do not specify one. + * We use GTK_SIZE_GROUP_HORIZONTAL, so that the widths are the same. */ + GtkSizeGroup *sizegroup = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); + + /* The outgoing server widgets: */ + if (!priv->entry_outgoingserver) + priv->entry_outgoingserver = gtk_entry_new (); + /* Auto-capitalization is the default, so let's turn it off: */ + hildon_gtk_entry_set_input_mode (GTK_ENTRY (priv->entry_outgoingserver), HILDON_GTK_INPUT_MODE_FULL); + g_signal_connect(G_OBJECT(priv->entry_outgoingserver), "changed", G_CALLBACK(on_change), self); + + GtkWidget *caption = hildon_caption_new (sizegroup, + _("mcen_li_emailsetup_smtp"), priv->entry_outgoingserver, NULL, HILDON_CAPTION_OPTIONAL); + gtk_widget_show (priv->entry_outgoingserver); + gtk_box_pack_start (GTK_BOX (vbox), caption, FALSE, FALSE, MODEST_MARGIN_HALF); + gtk_widget_show (caption); + + /* The secure authentication widgets: */ + if (!priv->combo_outgoing_auth) + priv->combo_outgoing_auth = GTK_WIDGET (modest_secureauth_combo_box_new ()); + caption = hildon_caption_new (sizegroup, _("mcen_li_emailsetup_secure_authentication"), + priv->combo_outgoing_auth, NULL, HILDON_CAPTION_OPTIONAL); + g_signal_connect (G_OBJECT (priv->combo_outgoing_auth), "changed", G_CALLBACK(on_change), self); + gtk_widget_show (priv->combo_outgoing_auth); + gtk_box_pack_start (GTK_BOX (vbox), caption, FALSE, FALSE, MODEST_MARGIN_HALF); + gtk_widget_show (caption); + + /* The username widgets: */ + priv->entry_user_username = GTK_WIDGET (modest_validating_entry_new ()); + /* Auto-capitalization is the default, so let's turn it off: */ + hildon_gtk_entry_set_input_mode (GTK_ENTRY (priv->entry_user_username), HILDON_GTK_INPUT_MODE_FULL); + caption = hildon_caption_new (sizegroup, _("mail_fi_username"), + priv->entry_user_username, NULL, HILDON_CAPTION_MANDATORY); + g_signal_connect(G_OBJECT(priv->entry_user_username), "changed", G_CALLBACK(on_change), self); + gtk_widget_show (priv->entry_user_username); + gtk_box_pack_start (GTK_BOX (vbox), caption, FALSE, FALSE, MODEST_MARGIN_HALF); + gtk_widget_show (caption); + + /* Prevent the use of some characters in the username, + * as required by our UI specification: */ + modest_validating_entry_set_unallowed_characters_whitespace ( + MODEST_VALIDATING_ENTRY (priv->entry_user_username)); + + /* Set max length as in the UI spec: + * TODO: The UI spec seems to want us to show a dialog if we hit the maximum. */ + gtk_entry_set_max_length (GTK_ENTRY (priv->entry_user_username), 64); + + /* The password widgets: */ + priv->entry_user_password = gtk_entry_new (); + /* Auto-capitalization is the default, so let's turn it off: */ + hildon_gtk_entry_set_input_mode (GTK_ENTRY (priv->entry_user_password), + HILDON_GTK_INPUT_MODE_FULL | HILDON_GTK_INPUT_MODE_INVISIBLE); + gtk_entry_set_visibility (GTK_ENTRY (priv->entry_user_password), FALSE); + /* gtk_entry_set_invisible_char (GTK_ENTRY (priv->entry_user_password), '*'); */ + caption = hildon_caption_new (sizegroup, + _("mail_fi_password"), priv->entry_user_password, NULL, HILDON_CAPTION_OPTIONAL); + g_signal_connect(G_OBJECT(priv->entry_user_password), "changed", G_CALLBACK(on_change), self); + gtk_widget_show (priv->entry_user_password); + gtk_box_pack_start (GTK_BOX (vbox), caption, FALSE, FALSE, MODEST_MARGIN_HALF); + gtk_widget_show (caption); + + /* The secure connection widgets: */ + if (!priv->combo_outgoing_security) + priv->combo_outgoing_security = GTK_WIDGET (modest_serversecurity_combo_box_new ()); + modest_serversecurity_combo_box_fill ( + MODEST_SERVERSECURITY_COMBO_BOX (priv->combo_outgoing_security), MODEST_PROTOCOLS_TRANSPORT_SMTP); + modest_serversecurity_combo_box_set_active_serversecurity ( + MODEST_SERVERSECURITY_COMBO_BOX (priv->combo_outgoing_security), MODEST_PROTOCOLS_CONNECTION_NONE); + caption = hildon_caption_new (sizegroup, _("mcen_li_emailsetup_secure_connection"), + priv->combo_outgoing_security, NULL, HILDON_CAPTION_OPTIONAL); + gtk_widget_show (priv->combo_outgoing_security); + gtk_box_pack_start (GTK_BOX (vbox), caption, FALSE, FALSE, MODEST_MARGIN_HALF); + gtk_widget_show (caption); + + /* The port number widgets: */ + if (!priv->entry_port) + priv->entry_port = GTK_WIDGET (hildon_number_editor_new (PORT_RANGE_MIN, PORT_RANGE_MAX)); + caption = hildon_caption_new (sizegroup, + _("mcen_fi_emailsetup_port"), priv->entry_port, NULL, HILDON_CAPTION_OPTIONAL); + gtk_widget_add_events(GTK_WIDGET(priv->entry_port), GDK_FOCUS_CHANGE_MASK); + g_signal_connect(G_OBJECT(priv->entry_port), "range-error", G_CALLBACK(on_range_error), self); + g_signal_connect(G_OBJECT(priv->entry_port), "notify::value", G_CALLBACK(on_value_changed), self); + gtk_widget_show (priv->entry_port); + gtk_box_pack_start (GTK_BOX (vbox), caption, FALSE, FALSE, MODEST_MARGIN_HALF); + gtk_widget_show (caption); + + /* Show a default port number when the security method changes, as per the UI spec: */ + g_signal_connect (G_OBJECT (priv->combo_outgoing_security), "changed", (GCallback)on_combo_security_changed, self); + on_combo_security_changed (GTK_COMBO_BOX (priv->combo_outgoing_security), self); + + /* Add the buttons: */ + gtk_dialog_add_button (GTK_DIALOG (self), _("mcen_bd_dialog_ok"), GTK_RESPONSE_OK); + gtk_dialog_add_button (GTK_DIALOG (self), _("mcen_bd_dialog_cancel"), GTK_RESPONSE_CANCEL); + + priv->is_dirty = FALSE; + priv->range_error_occured = FALSE; + g_signal_connect(G_OBJECT(self), "response", G_CALLBACK(on_response), self); + g_signal_connect(G_OBJECT(vbox), "set-focus-child", G_CALLBACK(on_set_focus_child), self); + + priv->range_error_banner_timeout = 0; + priv->account_name = NULL; + + scrolled_window = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scrolled_window), vbox); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); + gtk_box_pack_start (GTK_BOX (dialog_box), scrolled_window, TRUE, TRUE, 0); + gtk_container_set_focus_vadjustment (GTK_CONTAINER (vbox), + gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (scrolled_window))); + + gtk_widget_show_all (dialog_box); + gtk_window_set_default_size (GTK_WINDOW (self), -1, 220); + + + /* When this window is shown, hibernation should not be possible, + * because there is no sensible way to save the state: */ + modest_window_mgr_prevent_hibernation_while_window_is_shown ( + modest_runtime_get_window_mgr (), GTK_WINDOW (self)); + + hildon_help_dialog_help_enable (GTK_DIALOG(self), + "applications_email_connectionspecificsmtpconf", + modest_maemo_utils_get_osso_context()); +} + +ModestConnectionSpecificSmtpEditWindow* +modest_connection_specific_smtp_edit_window_new (void) +{ + return g_object_new (MODEST_TYPE_CONNECTION_SPECIFIC_SMTP_EDIT_WINDOW, NULL); +} + +void +modest_connection_specific_smtp_edit_window_set_connection ( + ModestConnectionSpecificSmtpEditWindow *window, const gchar* iap_id, const gchar* iap_name, + ModestServerAccountSettings *server_settings) +{ + ModestConnectionSpecificSmtpEditWindowPrivate *priv = + CONNECTION_SPECIFIC_SMTP_EDIT_WINDOW_GET_PRIVATE (window); + + /* This causes a warning because of the %s in the translation, but not in the original string: */ + gchar* title = g_strdup_printf (_("mcen_ti_connection_connection_name"), iap_name); + gtk_window_set_title (GTK_WINDOW (window), title); + g_free (title); + + if (server_settings) + { + + if (priv->account_name) + g_free (priv->account_name); + priv->account_name = g_strdup (modest_server_account_settings_get_account_name (server_settings)); + gtk_entry_set_text (GTK_ENTRY (priv->entry_outgoingserver), + modest_server_account_settings_get_hostname (server_settings)); + gtk_entry_set_text (GTK_ENTRY (priv->entry_user_username), + modest_server_account_settings_get_username (server_settings)); + gtk_entry_set_text (GTK_ENTRY (priv->entry_user_password), + modest_server_account_settings_get_password (server_settings)); + + modest_serversecurity_combo_box_set_active_serversecurity ( + MODEST_SERVERSECURITY_COMBO_BOX (priv->combo_outgoing_security), + modest_server_account_settings_get_security_protocol (server_settings)); + + modest_secureauth_combo_box_set_active_secureauth ( + MODEST_SECUREAUTH_COMBO_BOX (priv->combo_outgoing_auth), + modest_server_account_settings_get_auth_protocol (server_settings)); + + /* port: */ + hildon_number_editor_set_value ( + HILDON_NUMBER_EDITOR (priv->entry_port), + modest_server_account_settings_get_port (server_settings)); + + + /* This will cause changed signals so we set dirty back to FALSE */ + priv->is_dirty = FALSE; + if (priv->range_error_banner_timeout > 0) { + g_source_remove (priv->range_error_banner_timeout); + priv->range_error_banner_timeout = 0; + } + + } +} + +ModestServerAccountSettings* +modest_connection_specific_smtp_edit_window_get_settings (ModestConnectionSpecificSmtpEditWindow *window) +{ + ModestConnectionSpecificSmtpEditWindowPrivate *priv = NULL; + ModestServerAccountSettings *server_settings = NULL; + const gchar *outgoing_server = NULL; + + priv = CONNECTION_SPECIFIC_SMTP_EDIT_WINDOW_GET_PRIVATE (window); + outgoing_server = gtk_entry_get_text (GTK_ENTRY (priv->entry_outgoingserver)); + + /* If the outgoing server is NULL, we are removing the connection specific + * settings */ + if ((outgoing_server == NULL) || (outgoing_server[0] == '\0')) { + return NULL; + } + + server_settings = modest_server_account_settings_new (); + + modest_server_account_settings_set_hostname (server_settings, + gtk_entry_get_text (GTK_ENTRY (priv->entry_outgoingserver))); + modest_server_account_settings_set_protocol (server_settings, + MODEST_PROTOCOLS_TRANSPORT_SMTP); + modest_server_account_settings_set_username (server_settings, + gtk_entry_get_text (GTK_ENTRY (priv->entry_user_username))); + modest_server_account_settings_set_password (server_settings, + gtk_entry_get_text (GTK_ENTRY (priv->entry_user_password))); + + modest_server_account_settings_set_security_protocol (server_settings, + modest_serversecurity_combo_box_get_active_serversecurity ( + MODEST_SERVERSECURITY_COMBO_BOX (priv->combo_outgoing_security))); + modest_server_account_settings_set_auth_protocol (server_settings, + modest_secureauth_combo_box_get_active_secureauth ( + MODEST_SECUREAUTH_COMBO_BOX (priv->combo_outgoing_auth))); + modest_server_account_settings_set_account_name (server_settings, + priv->account_name); + + /* port: */ + modest_server_account_settings_set_port (server_settings, + hildon_number_editor_get_value (HILDON_NUMBER_EDITOR (priv->entry_port))); + + return server_settings; +} + +gboolean +modest_connection_specific_smtp_edit_window_is_dirty(ModestConnectionSpecificSmtpEditWindow *window) +{ + ModestConnectionSpecificSmtpEditWindowPrivate *priv = + CONNECTION_SPECIFIC_SMTP_EDIT_WINDOW_GET_PRIVATE (window); + + return priv->is_dirty; +} diff --git a/src/hildon2/modest-connection-specific-smtp-edit-window.h b/src/hildon2/modest-connection-specific-smtp-edit-window.h new file mode 100644 index 0000000..4efcee5 --- /dev/null +++ b/src/hildon2/modest-connection-specific-smtp-edit-window.h @@ -0,0 +1,86 @@ +/* Copyright (c) 2006, Nokia Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Nokia Corporation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __MODEST_MAEMO_CONNECTION_SPECIFIC_SMTP_EDIT_WINDOW +#define __MODEST_MAEMO_CONNECTION_SPECIFIC_SMTP_EDIT_WINDOW + +#include +#include +#include + +G_BEGIN_DECLS + +#define MODEST_TYPE_CONNECTION_SPECIFIC_SMTP_EDIT_WINDOW modest_connection_specific_smtp_edit_window_get_type() + +#define MODEST_CONNECTION_SPECIFIC_SMTP_EDIT_WINDOW(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + MODEST_TYPE_CONNECTION_SPECIFIC_SMTP_EDIT_WINDOW, ModestConnectionSpecificSmtpEditWindow)) + +#define MODEST_CONNECTION_SPECIFIC_SMTP_EDIT_WINDOW_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), \ + MODEST_TYPE_CONNECTION_SPECIFIC_SMTP_EDIT_WINDOW, ModestConnectionSpecificSmtpEditWindowClass)) + +#define MODEST_IS_CONNECTION_SPECIFIC_SMTP_EDIT_WINDOW(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + MODEST_TYPE_CONNECTION_SPECIFIC_SMTP_EDIT_WINDOW)) + +#define MODEST_IS_CONNECTION_SPECIFIC_SMTP_EDIT_WINDOW_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + MODEST_TYPE_CONNECTION_SPECIFIC_SMTP_EDIT_WINDOW)) + +#define MODEST_CONNECTION_SPECIFIC_SMTP_EDIT_WINDOW_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + MODEST_TYPE_CONNECTION_SPECIFIC_SMTP_EDIT_WINDOW, ModestConnectionSpecificSmtpEditWindowClass)) + +typedef struct { + GtkDialog parent; + +} ModestConnectionSpecificSmtpEditWindow; + +typedef struct { + GtkDialogClass parent_class; +} ModestConnectionSpecificSmtpEditWindowClass; + +GType modest_connection_specific_smtp_edit_window_get_type (void); + +ModestConnectionSpecificSmtpEditWindow* modest_connection_specific_smtp_edit_window_new (void); + +void modest_connection_specific_smtp_edit_window_set_connection ( + ModestConnectionSpecificSmtpEditWindow *window, const gchar* iap_id, const gchar* iap_name, + ModestServerAccountSettings *server_settings); + +ModestServerAccountSettings* modest_connection_specific_smtp_edit_window_get_settings ( + ModestConnectionSpecificSmtpEditWindow *window); + +gboolean modest_connection_specific_smtp_edit_window_is_dirty( + ModestConnectionSpecificSmtpEditWindow *window); + +G_END_DECLS + +#endif /* __MODEST_MAEMO_CONNECTION_SPECIFIC_SMTP_WINDOW */ diff --git a/src/hildon2/modest-connection-specific-smtp-window.c b/src/hildon2/modest-connection-specific-smtp-window.c new file mode 100644 index 0000000..3f98ded --- /dev/null +++ b/src/hildon2/modest-connection-specific-smtp-window.c @@ -0,0 +1,581 @@ +/* Copyright (c) 2006, Nokia Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Nokia Corporation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "modest-connection-specific-smtp-window.h" +#include "modest-connection-specific-smtp-edit-window.h" +#include +#include "widgets/modest-ui-constants.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "modest-hildon-includes.h" +#include "modest-platform.h" +#include "modest-maemo-utils.h" + +#include +#include + +G_DEFINE_TYPE (ModestConnectionSpecificSmtpWindow, modest_connection_specific_smtp_window, + GTK_TYPE_DIALOG); + +#define CONNECTION_SPECIFIC_SMTP_WINDOW_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), MODEST_TYPE_CONNECTION_SPECIFIC_SMTP_WINDOW, ModestConnectionSpecificSmtpWindowPrivate)) + +typedef struct _ModestConnectionSpecificSmtpWindowPrivate ModestConnectionSpecificSmtpWindowPrivate; + +struct _ModestConnectionSpecificSmtpWindowPrivate +{ + GtkTreeView *treeview; + GtkTreeModel *model; + GtkWidget *button_edit; + + ModestAccountMgr *account_manager; +}; + +static void on_response (GtkDialog *dialog, + gint response, + gpointer user_data); + +/* static gboolean on_key_pressed (GtkWidget *self, GdkEventKey *event, gpointer user_data); */ + +static void +modest_connection_specific_smtp_window_get_property (GObject *object, guint property_id, + GValue *value, GParamSpec *pspec) +{ + switch (property_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +modest_connection_specific_smtp_window_set_property (GObject *object, guint property_id, + const GValue *value, GParamSpec *pspec) +{ + switch (property_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +modest_connection_specific_smtp_window_dispose (GObject *object) +{ + if (G_OBJECT_CLASS (modest_connection_specific_smtp_window_parent_class)->dispose) + G_OBJECT_CLASS (modest_connection_specific_smtp_window_parent_class)->dispose (object); +} + +enum MODEL_COLS { + MODEL_COL_NAME = 0, /* libconic IAP Name: a string */ + MODEL_COL_ID = 1, /* libconic IAP ID: a string */ + MODEL_COL_SERVER_ACCOUNT_NAME = 2, /* a string */ + MODEL_COL_SERVER_NAME = 3, /* a string */ + MODEL_COL_SERVER_ACCOUNT_SETTINGS = 4 /* a gpointer */ +}; + + +void update_model_server_names (ModestConnectionSpecificSmtpWindow *self); + +static void +modest_connection_specific_smtp_window_finalize (GObject *object) +{ + ModestConnectionSpecificSmtpWindowPrivate *priv = CONNECTION_SPECIFIC_SMTP_WINDOW_GET_PRIVATE (object); + + /* Free all the data items from the treemodel: */ + GtkTreeIter iter; + gboolean valid = gtk_tree_model_get_iter_first (priv->model, &iter); + while (valid) { + ModestServerAccountSettings *server_settings = NULL; + + gtk_tree_model_get (priv->model, &iter, + MODEL_COL_SERVER_ACCOUNT_SETTINGS, &server_settings, + -1); + + if (server_settings) + g_object_unref (server_settings); + + /* Get next row: */ + valid = gtk_tree_model_iter_next (priv->model, &iter); + } + + g_object_unref (G_OBJECT (priv->model)); + + G_OBJECT_CLASS (modest_connection_specific_smtp_window_parent_class)->finalize (object); +} + +static void +modest_connection_specific_smtp_window_class_init (ModestConnectionSpecificSmtpWindowClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (ModestConnectionSpecificSmtpWindowPrivate)); + + object_class->get_property = modest_connection_specific_smtp_window_get_property; + object_class->set_property = modest_connection_specific_smtp_window_set_property; + object_class->dispose = modest_connection_specific_smtp_window_dispose; + object_class->finalize = modest_connection_specific_smtp_window_finalize; +} + +/* libconic does not return a list of connections in scratchbox, + * so enable this to put a fake row in the list, + * so we can test other parts of the code. */ +/* #define DEBUG_WITHOUT_LIBCONIC 1 */ + +void +modest_connection_specific_smtp_window_fill_with_connections (ModestConnectionSpecificSmtpWindow *self, + ModestAccountMgr *account_manager) +{ +#ifdef MODEST_HAVE_CONIC + ModestConnectionSpecificSmtpWindowPrivate *priv = + CONNECTION_SPECIFIC_SMTP_WINDOW_GET_PRIVATE (self); + priv->account_manager = account_manager; + + GtkListStore *liststore = GTK_LIST_STORE (priv->model); + + TnyDevice *device = modest_runtime_get_device (); + g_assert (TNY_IS_MAEMO_CONIC_DEVICE (device)); + + /* Get the list of Internet Access Points: */ + #ifdef DEBUG_WITHOUT_LIBCONIC + GSList *list_iaps = g_slist_append(NULL, (gpointer)1); + #else + TnyMaemoConicDevice *maemo_device = TNY_MAEMO_CONIC_DEVICE (device); + GSList *list_iaps = tny_maemo_conic_device_get_iap_list (maemo_device); + #endif + + /* printf("debug: list_iaps=%p, list_iaps size = %d\n", list_iaps, g_slist_length(list_iaps)); */ + + GSList* iter = list_iaps; + while (iter) { + ConIcIap *iap = (ConIcIap*)iter->data; + if (iap) { + #ifdef DEBUG_WITHOUT_LIBCONIC + const gchar *connection_name = "debug name"; + const gchar *connection_id = "debug id"; + #else + const gchar *connection_name = con_ic_iap_get_name (iap); + const gchar *connection_id = con_ic_iap_get_id (iap); + #endif + + printf ("debug: iac name=%s, id=%s\n", connection_name, connection_id); + + /* Get any already-associated connection-specific server account: */ + gchar *server_account_name = NULL; + server_account_name = modest_account_mgr_get_connection_specific_smtp ( + priv->account_manager, connection_id); + + /* Add the row to the model: */ + GtkTreeIter iter; + gtk_list_store_append (liststore, &iter); + gtk_list_store_set(liststore, &iter, + MODEL_COL_ID, connection_id, + MODEL_COL_NAME, connection_name, + MODEL_COL_SERVER_ACCOUNT_NAME, server_account_name, + -1); + + g_free (server_account_name); + } + + iter = g_slist_next (iter); + } + + #ifndef DEBUG_WITHOUT_LIBCONIC + if (list_iaps) + tny_maemo_conic_device_free_iap_list (maemo_device, list_iaps); + #endif + + update_model_server_names (self); +#endif /*MODEST_HAVE_CONIC */ +} + +static void +on_button_edit (ModestConnectionSpecificSmtpWindow *self) +{ + ModestConnectionSpecificSmtpWindowPrivate *priv = CONNECTION_SPECIFIC_SMTP_WINDOW_GET_PRIVATE (self); + ModestAccountMgr *mgr = modest_runtime_get_account_mgr (); + + gchar *id = NULL; + gchar *connection_name = NULL; + gchar *server_account_name = NULL; + ModestServerAccountSettings *server_settings = NULL; + GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->treeview)); + GtkTreeIter iter; + GtkTreeModel *model = 0; + if (gtk_tree_selection_get_selected (sel, &model, &iter)) { + gtk_tree_model_get (priv->model, &iter, + MODEL_COL_ID, &id, + MODEL_COL_NAME, &connection_name, + MODEL_COL_SERVER_ACCOUNT_NAME, &server_account_name, + MODEL_COL_SERVER_ACCOUNT_SETTINGS, &server_settings, + -1); + + /* printf("DEBUG: %s: BEFORE: connection-specific server_account_name=%s\n", __FUNCTION__, server_account_name); */ + /* TODO: Is 0 an allowed libconic IAP ID? + * If not then we should check for it. */ + + /* Get existing server account data if a server account is already specified: */ + gboolean settings_were_retrieved = FALSE; + if (server_account_name && !server_settings) { + server_settings = modest_account_mgr_load_server_settings(mgr, server_account_name, TRUE); + if (server_settings) + settings_were_retrieved = TRUE; + } + + GtkWidget * window = GTK_WIDGET (modest_connection_specific_smtp_edit_window_new ()); + modest_connection_specific_smtp_edit_window_set_connection ( + MODEST_CONNECTION_SPECIFIC_SMTP_EDIT_WINDOW (window), id, connection_name, server_settings); + + /* Delete data, unless it was data from the rowmodel: */ + if (settings_were_retrieved) { + g_object_unref (server_settings); + server_settings = NULL; + } + + modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), GTK_WINDOW (window)); + + gint response = gtk_dialog_run (GTK_DIALOG (window)); + if (response == GTK_RESPONSE_OK) { + + /* Delete any previous data for this row: */ + if (server_settings) { + g_object_unref (server_settings); + server_settings = NULL; + } + + /* Get the new account data and save it in the row for later: + * We free this in finalize(), + * and save it to our configuration in + * modest_connection_specific_smtp_window_save_server_accounts(). */ + server_settings = modest_connection_specific_smtp_edit_window_get_settings ( + MODEST_CONNECTION_SPECIFIC_SMTP_EDIT_WINDOW (window)); + + if (server_settings) { + gtk_list_store_set (GTK_LIST_STORE (priv->model), &iter, + MODEL_COL_SERVER_ACCOUNT_SETTINGS, server_settings, + MODEL_COL_SERVER_NAME, modest_server_account_settings_get_hostname (server_settings), + -1); + } else { + gtk_list_store_set (GTK_LIST_STORE (priv->model), &iter, + MODEL_COL_SERVER_ACCOUNT_SETTINGS, NULL, + MODEL_COL_SERVER_NAME, NULL, + MODEL_COL_SERVER_ACCOUNT_NAME, NULL, + -1); + } + } + gtk_widget_destroy (window); + } + g_free (connection_name); + g_free (id); + g_free (server_account_name); + update_model_server_names (self); +} + +static void +on_selection_changed (GtkTreeSelection *sel, ModestConnectionSpecificSmtpWindow *self) +{ + ModestConnectionSpecificSmtpWindowPrivate *priv = + CONNECTION_SPECIFIC_SMTP_WINDOW_GET_PRIVATE (self); + + GtkTreeModel *model = NULL; + GtkTreeIter iter; + const gboolean has_selection = + gtk_tree_selection_get_selected (sel, &model, &iter); + + gtk_widget_set_sensitive (priv->button_edit, has_selection); +} + +static void +modest_connection_specific_smtp_window_init (ModestConnectionSpecificSmtpWindow *self) +{ + ModestWindowMgr *mgr; + + /* Specify a default size, because the GtkTreeView's default requested size + * is not big enough: */ + gtk_window_set_default_size (GTK_WINDOW (self), 500, 300); + + /* This seems to be necessary to make the window show at the front with decoration. + * If we use property type=GTK_WINDOW_TOPLEVEL instead of the default GTK_WINDOW_POPUP+decoration, + * then the window will be below the others. */ + gtk_window_set_type_hint (GTK_WINDOW (self), + GDK_WINDOW_TYPE_HINT_DIALOG); + + ModestConnectionSpecificSmtpWindowPrivate *priv = + CONNECTION_SPECIFIC_SMTP_WINDOW_GET_PRIVATE (self); + + /* Create a tree model for the tree view: + * with a string for the name, a string for the server name, and an int for the ID. + * This must match our MODEL_COLS enum constants. + */ + priv->model = GTK_TREE_MODEL (gtk_list_store_new (5, + G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER)); + + /* Setup the tree view: */ + priv->treeview = GTK_TREE_VIEW (gtk_tree_view_new_with_model (priv->model)); + + /* Show the column headers, + * which does not seem to be the default on Maemo. + */ + gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(priv->treeview), TRUE); + + /* name column: + * The ID model column in not shown in the view. */ + GtkTreeViewColumn *view_column = gtk_tree_view_column_new (); + GtkCellRenderer *renderer = gtk_cell_renderer_text_new (); + gtk_tree_view_column_pack_start(view_column, renderer, TRUE); + gtk_tree_view_column_set_attributes (view_column, renderer, + "text", MODEL_COL_NAME, NULL); + gtk_tree_view_column_set_title (view_column, _("mcen_ia_optionalsmtp_connection_name")); + gtk_tree_view_append_column (priv->treeview, view_column); + + + /* server name column: */ + view_column = gtk_tree_view_column_new (); + renderer = gtk_cell_renderer_text_new (); + gtk_tree_view_column_pack_start(view_column, renderer, TRUE); + gtk_tree_view_column_set_attributes (view_column, renderer, + "text", MODEL_COL_SERVER_NAME, NULL); + gtk_tree_view_column_set_title (view_column, _("mcen_ia_optionalsmtp_servername")); + gtk_tree_view_append_column (priv->treeview, view_column); + + /* The application must call modest_connection_specific_smtp_window_fill_with_connections(). */ + + GtkWidget *vbox = GTK_DIALOG(self)->vbox; + //gtk_vbox_new (FALSE, MODEST_MARGIN_DEFAULT); + + /* Introductory note: */ + /* TODO: For some reason this label does not wrap. It is truncated. */ + GtkWidget *label = gtk_label_new(_("mcen_ia_optionalsmtp_note")); + gtk_label_set_line_wrap (GTK_LABEL (label), TRUE); + /* So that it is shown without being truncated: */ + gtk_label_set_max_width_chars (GTK_LABEL (label), 20); + /* The documentation for gtk_label_set_line_wrap() says that we must + * call gtk_widget_set_size_request() with a hard-coded width, + * though I wonder why gtk_label_set_max_width_chars() isn't enough. */ + gtk_widget_set_size_request (label, 400, -1); + gtk_widget_show (label); + gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, MODEST_MARGIN_HALF); + + /* Put the treeview in a scrolled window and add it to the box: */ + GtkWidget *scrolled_window = gtk_scrolled_window_new (NULL, NULL); + gtk_container_set_border_width (GTK_CONTAINER (scrolled_window), MODEST_MARGIN_DEFAULT); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window), GTK_POLICY_NEVER, + GTK_POLICY_AUTOMATIC); + gtk_widget_show (scrolled_window); + gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (priv->treeview)); + gtk_box_pack_start (GTK_BOX (vbox), GTK_WIDGET (scrolled_window), TRUE, TRUE, MODEST_MARGIN_HALF); + gtk_widget_show (GTK_WIDGET (priv->treeview)); + gtk_widget_show (vbox); + + /* Hack: we use the response apply to identify the click on the edit button */ + priv->button_edit = gtk_dialog_add_button (GTK_DIALOG(self), _("mcen_bd_edit"), GTK_RESPONSE_APPLY); + gtk_dialog_add_button (GTK_DIALOG(self), _("mcen_bd_close"), GTK_RESPONSE_CLOSE); + + /* Disable the Edit button when nothing is selected: */ + GtkTreeSelection *sel = gtk_tree_view_get_selection (priv->treeview); + g_signal_connect (sel, "changed", + G_CALLBACK(on_selection_changed), self); + on_selection_changed (sel, self); + + /* When this window is shown, hibernation should not be possible, + * because there is no sensible way to save the state: */ + mgr = modest_runtime_get_window_mgr (); + modest_window_mgr_prevent_hibernation_while_window_is_shown (mgr, + GTK_WINDOW (self)); + + /* Set window title */ + gtk_window_set_title (GTK_WINDOW (self), _("mcen_ti_optionalsmtp_servers")); + + g_signal_connect (self, "response", G_CALLBACK (on_response), NULL); + + hildon_help_dialog_help_enable (GTK_DIALOG(self), + "email_connectionsspecificsmtpconf", + modest_maemo_utils_get_osso_context()); +} + +ModestConnectionSpecificSmtpWindow* +modest_connection_specific_smtp_window_new (void) +{ + return g_object_new (MODEST_TYPE_CONNECTION_SPECIFIC_SMTP_WINDOW, NULL); +} + +/** The application should call this when the user changes should be saved. + */ +gboolean +modest_connection_specific_smtp_window_save_server_accounts (ModestConnectionSpecificSmtpWindow *self) +{ + ModestAccountMgr *mgr = modest_runtime_get_account_mgr (); + ModestConnectionSpecificSmtpWindowPrivate *priv = + CONNECTION_SPECIFIC_SMTP_WINDOW_GET_PRIVATE (self); + + + /* Get the first iter in the list */ + GtkTreeIter iter; + gboolean valid = gtk_tree_model_get_iter_first (priv->model, &iter); + + /* Walk through the list, reading each row */ + while (valid) { + gchar *id = NULL; + gchar *connection_name = NULL; + gchar *server_account_name = NULL; + gchar *server_name = NULL; + ModestServerAccountSettings *server_settings = NULL; + + gtk_tree_model_get (priv->model, &iter, + MODEL_COL_ID, &id, + MODEL_COL_NAME, &connection_name, + MODEL_COL_SERVER_NAME, &server_name, + MODEL_COL_SERVER_ACCOUNT_NAME, &server_account_name, + MODEL_COL_SERVER_ACCOUNT_SETTINGS, &server_settings, + -1); + + gboolean success = TRUE; + if (id && server_settings) { /* The presence of data suggests that there is something to save. */ + if (!server_account_name) { + /* Add a new server account, building a (non-human-visible) name: */ + gchar *name_start = g_strdup_printf("specific_%s", connection_name); + server_account_name = modest_account_mgr_get_unused_account_name ( + priv->account_manager, name_start, TRUE /* server account. */); + g_assert (server_account_name); + g_free (name_start); + + modest_server_account_settings_set_account_name (server_settings, server_account_name); + success = modest_account_mgr_save_server_settings (mgr, server_settings); + if (success) { + TnyAccount *account = TNY_ACCOUNT (modest_tny_account_store_new_connection_specific_transport_account + (modest_runtime_get_account_store (), + server_account_name)); + if (account) + g_object_unref (account); + } + + /* associate the specific server account with this connection for this account: */ + success = success && modest_account_mgr_set_connection_specific_smtp ( + priv->account_manager, id, server_account_name); + + /* Save the new name in the treemodel, so it can be edited again later: */ + gtk_list_store_set (GTK_LIST_STORE (priv->model), &iter, + MODEL_COL_SERVER_ACCOUNT_NAME, server_account_name, -1); + + } else { + modest_account_mgr_save_server_settings (mgr, server_settings); + } + } else if (id && server_name && + !strcmp (server_name, _("mcen_ia_optionalsmtp_notdefined"))) { + modest_account_mgr_remove_connection_specific_smtp (priv->account_manager, + id); + gtk_list_store_set (GTK_LIST_STORE (priv->model), &iter, + MODEL_COL_SERVER_ACCOUNT_NAME, NULL, -1); + } + + g_free (connection_name); + g_free (id); + g_free (server_account_name); + g_free (server_name); + + if (!success) + return FALSE; + + /* Get next row: */ + valid = gtk_tree_model_iter_next (priv->model, &iter); + } + + update_model_server_names (self); + + return TRUE; +} + +void update_model_server_names (ModestConnectionSpecificSmtpWindow *self) +{ + ModestConnectionSpecificSmtpWindowPrivate *priv = CONNECTION_SPECIFIC_SMTP_WINDOW_GET_PRIVATE (self); + + GtkTreeIter iter; + gboolean valid = gtk_tree_model_get_iter_first (priv->model, &iter); + while (valid) { + + gchar *server_account_name = NULL; + ModestServerAccountSettings *server_settings = NULL; + gtk_tree_model_get (priv->model, &iter, + MODEL_COL_SERVER_ACCOUNT_NAME, &server_account_name, + MODEL_COL_SERVER_ACCOUNT_SETTINGS, &server_settings, + -1); + if (server_settings && modest_server_account_settings_get_hostname (server_settings) + && (modest_server_account_settings_get_hostname (server_settings) [0] != '\0')) { + gtk_list_store_set (GTK_LIST_STORE (priv->model), &iter, + MODEL_COL_SERVER_NAME, modest_server_account_settings_get_hostname (server_settings), + -1); + } else if (server_account_name) { + + /* Get the server hostname and show it in the treemodel: */ + gchar *hostname = modest_account_mgr_get_server_account_hostname (priv->account_manager, + server_account_name); + gtk_list_store_set (GTK_LIST_STORE (priv->model), &iter, + MODEL_COL_SERVER_NAME, hostname, + -1); + g_free (hostname); + } else { + gtk_list_store_set (GTK_LIST_STORE (priv->model), &iter, + MODEL_COL_SERVER_NAME, _("mcen_ia_optionalsmtp_notdefined"), + -1); + } + + /* Get next row: */ + valid = gtk_tree_model_iter_next (priv->model, &iter); + } +} + +static void +on_response (GtkDialog *dialog, + gint response, + gpointer user_data) +{ + switch (response) { + case GTK_RESPONSE_APPLY: + /* We use it for the edit button */ + on_button_edit (MODEST_CONNECTION_SPECIFIC_SMTP_WINDOW (dialog)); + g_signal_stop_emission_by_name (dialog, "response"); + break; + case GTK_RESPONSE_CLOSE: + case GTK_RESPONSE_NONE: + case GTK_RESPONSE_DELETE_EVENT: + /* Generated as a response to delete-event, i.e, + pressin Esc, or by pressing the Close button */ + modest_connection_specific_smtp_window_save_server_accounts (MODEST_CONNECTION_SPECIFIC_SMTP_WINDOW (dialog)); + gtk_widget_destroy (GTK_WIDGET (dialog)); + } +} diff --git a/src/hildon2/modest-connection-specific-smtp-window.h b/src/hildon2/modest-connection-specific-smtp-window.h new file mode 100644 index 0000000..279681e --- /dev/null +++ b/src/hildon2/modest-connection-specific-smtp-window.h @@ -0,0 +1,84 @@ +/* Copyright (c) 2006, Nokia Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Nokia Corporation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __MODEST_MAEMO_CONNECTION_SPECIFIC_SMTP_WINDOW +#define __MODEST_MAEMO_CONNECTION_SPECIFIC_SMTP_WINDOW + +#include +#include +#include +#include + +G_BEGIN_DECLS + +#define MODEST_TYPE_CONNECTION_SPECIFIC_SMTP_WINDOW modest_connection_specific_smtp_window_get_type() + +#define MODEST_CONNECTION_SPECIFIC_SMTP_WINDOW(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + MODEST_TYPE_CONNECTION_SPECIFIC_SMTP_WINDOW, ModestConnectionSpecificSmtpWindow)) + +#define MODEST_CONNECTION_SPECIFIC_SMTP_WINDOW_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), \ + MODEST_TYPE_CONNECTION_SPECIFIC_SMTP_WINDOW, ModestConnectionSpecificSmtpWindowClass)) + +#define MODEST_IS_CONNECTION_SPECIFIC_SMTP_WINDOW(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + MODEST_TYPE_CONNECTION_SPECIFIC_SMTP_WINDOW)) + +#define MODEST_IS_CONNECTION_SPECIFIC_SMTP_WINDOW_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + MODEST_TYPE_CONNECTION_SPECIFIC_SMTP_WINDOW)) + +#define MODEST_CONNECTION_SPECIFIC_SMTP_WINDOW_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + MODEST_TYPE_CONNECTION_SPECIFIC_SMTP_WINDOW, ModestConnectionSpecificSmtpWindowClass)) + +typedef struct { + GtkDialog parent; + +} ModestConnectionSpecificSmtpWindow; + +typedef struct { + GtkDialogClass parent_class; +} ModestConnectionSpecificSmtpWindowClass; + +GType modest_connection_specific_smtp_window_get_type (void); + +ModestConnectionSpecificSmtpWindow* modest_connection_specific_smtp_window_new (void); + +void +modest_connection_specific_smtp_window_fill_with_connections (ModestConnectionSpecificSmtpWindow *self, + ModestAccountMgr *account_manager); + +gboolean +modest_connection_specific_smtp_window_save_server_accounts (ModestConnectionSpecificSmtpWindow *self); + +G_END_DECLS + +#endif /* __MODEST_MAEMO_CONNECTION_SPECIFIC_SMTP_WINDOW */ diff --git a/src/hildon2/modest-default-account-settings-dialog.c b/src/hildon2/modest-default-account-settings-dialog.c new file mode 100644 index 0000000..bdfb231 --- /dev/null +++ b/src/hildon2/modest-default-account-settings-dialog.c @@ -0,0 +1,1566 @@ +/* Copyright (c) 2008, Nokia Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Nokia Corporation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "modest-hildon-includes.h" +#include "modest-default-account-settings-dialog.h" +#include "modest-account-mgr.h" +#include "widgets/modest-serversecurity-combo-box.h" +#include "widgets/modest-secureauth-combo-box.h" +#include "widgets/modest-validating-entry.h" +#include "widgets/modest-retrieve-combo-box.h" +#include "widgets/modest-limit-retrieve-combo-box.h" +#include "modest-text-utils.h" +#include "modest-account-mgr.h" +#include "modest-account-mgr-helpers.h" /* For modest_account_mgr_get_account_data(). */ +#include +#include "modest-runtime.h" /* For modest_runtime_get_account_mgr(). */ +#include "maemo/modest-connection-specific-smtp-window.h" +#include "maemo/modest-signature-editor-dialog.h" +#include +#include +#include "maemo/modest-maemo-utils.h" +#include "maemo/modest-maemo-security-options-view.h" +#include "widgets/modest-ui-constants.h" +#include +#include + +#include +#include /* For strlen(). */ + +/* Include config.h so that _() works: */ +#ifdef HAVE_CONFIG_H +#include +#endif + +#define PORT_MIN 1 +#define PORT_MAX 65535 + +static void modest_account_settings_dialog_init (gpointer g, gpointer iface_data); + +G_DEFINE_TYPE_EXTENDED (ModestDefaultAccountSettingsDialog, + modest_default_account_settings_dialog, + GTK_TYPE_DIALOG, + 0, + G_IMPLEMENT_INTERFACE (MODEST_TYPE_ACCOUNT_SETTINGS_DIALOG, + modest_account_settings_dialog_init)); + +#define MODEST_DEFAULT_ACCOUNT_SETTINGS_DIALOG_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), MODEST_TYPE_DEFAULT_ACCOUNT_SETTINGS_DIALOG, ModestDefaultAccountSettingsDialogPrivate)) + +typedef struct _ModestDefaultAccountSettingsDialogPrivate ModestDefaultAccountSettingsDialogPrivate; + +struct _ModestDefaultAccountSettingsDialogPrivate +{ + /* Used by derived widgets to query existing accounts, + * and to create new accounts: */ + ModestAccountMgr *account_manager; + ModestAccountSettings *settings; + + gboolean modified; + gchar * account_name; /* This may not change. It is not user visible. */ + ModestProtocolType incoming_protocol; /* This may not change. */ + ModestProtocolType outgoing_protocol; /* This may not change. */ + gchar * original_account_title; + + ModestProtocolType protocol_authentication_incoming; + + GtkNotebook *notebook; + + GtkWidget *page_account_details; + GtkWidget *entry_account_title; + GtkWidget *combo_retrieve; + GtkWidget *combo_limit_retrieve; + GtkWidget *caption_leave_messages; + GtkWidget *checkbox_leave_messages; + + GtkWidget *page_user_details; + GtkWidget *entry_user_name; + GtkWidget *entry_user_username; + GtkWidget *entry_user_password; + GtkWidget *entry_user_email; +/* GtkWidget *entry_incoming_port; */ + GtkWidget *button_signature; + + GtkWidget *page_complete_easysetup; + + GtkWidget *page_incoming; + GtkWidget *caption_incoming; + GtkWidget *entry_incomingserver; +/* GtkWidget *combo_incoming_security; */ +/* GtkWidget *checkbox_incoming_auth; */ + + GtkWidget *page_outgoing; + GtkWidget *entry_outgoingserver; +/* GtkWidget *caption_outgoing_username; */ +/* GtkWidget *entry_outgoing_username; */ +/* GtkWidget *caption_outgoing_password; */ +/* GtkWidget *entry_outgoing_password; */ +/* GtkWidget *combo_outgoing_security; */ +/* GtkWidget *combo_outgoing_auth; */ +/* GtkWidget *entry_outgoing_port; */ + GtkWidget *checkbox_outgoing_smtp_specific; + GtkWidget *button_outgoing_smtp_servers; + + GtkWidget *signature_dialog; + + GtkWidget *incoming_security; + GtkWidget *outgoing_security; +}; + +static void +enable_buttons (ModestDefaultAccountSettingsDialog *self); + +static gboolean +save_configuration (ModestDefaultAccountSettingsDialog *dialog); + +static const gchar * null_means_empty (const gchar * str); + +static const gchar * +null_means_empty (const gchar * str) +{ + return str ? str : ""; +} + +static void +modest_default_account_settings_dialog_dispose (GObject *object) +{ + if (G_OBJECT_CLASS (modest_default_account_settings_dialog_parent_class)->dispose) + G_OBJECT_CLASS (modest_default_account_settings_dialog_parent_class)->dispose (object); +} + +static void +modest_default_account_settings_dialog_finalize (GObject *object) +{ + ModestDefaultAccountSettingsDialog *self; + ModestDefaultAccountSettingsDialogPrivate *priv; + + self = MODEST_DEFAULT_ACCOUNT_SETTINGS_DIALOG (object); + priv = MODEST_DEFAULT_ACCOUNT_SETTINGS_DIALOG_GET_PRIVATE (self); + + if (priv->account_name) + g_free (priv->account_name); + + if (priv->original_account_title) + g_free (priv->original_account_title); + + if (priv->account_manager) + g_object_unref (G_OBJECT (priv->account_manager)); + + if (priv->signature_dialog) + gtk_widget_destroy (priv->signature_dialog); + + if (priv->settings) { + g_object_unref (priv->settings); + priv->settings = NULL; + } + + G_OBJECT_CLASS (modest_default_account_settings_dialog_parent_class)->finalize (object); +} + +static void +set_modified (ModestDefaultAccountSettingsDialog *self, gboolean modified) +{ + ModestDefaultAccountSettingsDialogPrivate *priv; + + priv = MODEST_DEFAULT_ACCOUNT_SETTINGS_DIALOG_GET_PRIVATE (self); + priv->modified = modified; +} + +static void +on_modified_combobox_changed (GtkComboBox *widget, gpointer user_data) +{ + set_modified (MODEST_DEFAULT_ACCOUNT_SETTINGS_DIALOG (user_data), TRUE); +} + +static void +on_modified_entry_changed (GtkEditable *editable, gpointer user_data) +{ + set_modified (MODEST_DEFAULT_ACCOUNT_SETTINGS_DIALOG (user_data), TRUE); +} + +static void +on_modified_checkbox_toggled (GtkToggleButton *togglebutton, gpointer user_data) +{ + set_modified (MODEST_DEFAULT_ACCOUNT_SETTINGS_DIALOG (user_data), TRUE); +} + +static void +on_modified_number_editor_changed (HildonNumberEditor *number_editor, gint new_value, gpointer user_data) +{ + set_modified (MODEST_DEFAULT_ACCOUNT_SETTINGS_DIALOG (user_data), TRUE); +} + +static void +on_number_editor_notify (HildonNumberEditor *editor, GParamSpec *arg1, gpointer user_data) +{ + ModestDefaultAccountSettingsDialog *dialog = MODEST_DEFAULT_ACCOUNT_SETTINGS_DIALOG (user_data); + gint value = hildon_number_editor_get_value (editor); + + gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), GTK_RESPONSE_OK, value > 0); +} + +/* Set a modified boolean whenever the widget is changed, + * so we can check for it later. + */ +static void +connect_for_modified (ModestDefaultAccountSettingsDialog *self, GtkWidget *widget) +{ + if (HILDON_IS_NUMBER_EDITOR (widget)) { + g_signal_connect (G_OBJECT (widget), "notify::value", + G_CALLBACK (on_modified_number_editor_changed), self); + g_signal_connect (G_OBJECT (widget), "notify", G_CALLBACK (on_number_editor_notify), self); + } + else if (GTK_IS_ENTRY (widget)) { + g_signal_connect (G_OBJECT (widget), "changed", + G_CALLBACK (on_modified_entry_changed), self); + } else if (GTK_IS_COMBO_BOX (widget)) { + g_signal_connect (G_OBJECT (widget), "changed", + G_CALLBACK (on_modified_combobox_changed), self); + } else if (GTK_IS_TOGGLE_BUTTON (widget)) { + g_signal_connect (G_OBJECT (widget), "toggled", + G_CALLBACK (on_modified_checkbox_toggled), self); + } +} + +static void +on_caption_entry_changed (GtkEditable *editable, gpointer user_data) +{ + ModestDefaultAccountSettingsDialog *self = MODEST_DEFAULT_ACCOUNT_SETTINGS_DIALOG (user_data); + g_assert(self); + enable_buttons(self); +} + +static void +on_caption_combobox_changed (GtkComboBox *widget, gpointer user_data) +{ + ModestDefaultAccountSettingsDialog *self = MODEST_DEFAULT_ACCOUNT_SETTINGS_DIALOG (user_data); + g_assert(self); + enable_buttons(self); +} + +/** This is a convenience function to create a caption containing a mandatory widget. + * When the widget is edited, the enable_buttons() vfunc will be called. + */ +static GtkWidget* +create_caption_new_with_asterisk(ModestDefaultAccountSettingsDialog *self, + GtkSizeGroup *group, + const gchar *value, + GtkWidget *control, + GtkWidget *icon, + HildonCaptionStatus flag) +{ + GtkWidget *caption = NULL; + + /* Note: Previously, the translated strings already contained the "*", + * Comment out this code if they do again. + */ + /* Add a * character to indicate mandatory fields, + * as specified in our "Email UI Specification": */ + if (flag == HILDON_CAPTION_MANDATORY) { + gchar* title = g_strdup_printf("%s*", value); + caption = hildon_caption_new (group, title, control, icon, flag); + g_free(title); + } + else + caption = hildon_caption_new (group, value, control, icon, flag); + + /* Connect to the appropriate changed signal for the widget, + * so we can ask for the prev/next buttons to be enabled/disabled appropriately: + */ + if (GTK_IS_ENTRY (control)) { + g_signal_connect (G_OBJECT (control), "changed", + G_CALLBACK (on_caption_entry_changed), self); + + } + else if (GTK_IS_COMBO_BOX (control)) { + g_signal_connect (G_OBJECT (control), "changed", + G_CALLBACK (on_caption_combobox_changed), self); + } + + return caption; +} + +static void +on_entry_invalid_account_title_character (ModestValidatingEntry *self, const gchar* character, gpointer user_data) +{ + gchar *tmp, *msg; + + tmp = g_strndup (account_title_forbidden_chars, ACCOUNT_TITLE_FORBIDDEN_CHARS_LENGTH); + msg = g_strdup_printf (_CS("ckdg_ib_illegal_characters_entered"), tmp); + + modest_platform_information_banner (GTK_WIDGET (self), NULL, msg); + + g_free (msg); + g_free (tmp); +} + +static void +on_entry_invalid_fullname_character (ModestValidatingEntry *self, const gchar* character, gpointer user_data) +{ + gchar *tmp, *msg; + + tmp = g_strndup (user_name_forbidden_chars, USER_NAME_FORBIDDEN_CHARS_LENGTH); + msg = g_strdup_printf (_CS("ckdg_ib_illegal_characters_entered"), tmp); + + modest_platform_information_banner (GTK_WIDGET (self), NULL, msg); + + g_free (msg); + g_free (tmp); +} + + +static void +on_entry_max (ModestValidatingEntry *self, gpointer user_data) +{ + modest_platform_information_banner (GTK_WIDGET (self), NULL, + _CS("ckdg_ib_maximum_characters_reached")); +} + +static GtkWidget* +create_page_account_details (ModestDefaultAccountSettingsDialog *self) +{ + ModestDefaultAccountSettingsDialogPrivate *priv; + GtkWidget *box; + GtkAdjustment *focus_adjustment = NULL; + GtkSizeGroup* sizegroup; + GtkWidget *scrollwin; + + priv = MODEST_DEFAULT_ACCOUNT_SETTINGS_DIALOG_GET_PRIVATE (self); + box = gtk_vbox_new (FALSE, MODEST_MARGIN_NONE); + sizegroup = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); + scrollwin = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrollwin), + GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); + + /* The description widgets: */ + priv->entry_account_title = GTK_WIDGET (modest_validating_entry_new ()); + /* Do use auto-capitalization: */ + hildon_gtk_entry_set_input_mode (GTK_ENTRY (priv->entry_account_title), + HILDON_GTK_INPUT_MODE_FULL | HILDON_GTK_INPUT_MODE_AUTOCAP); + GtkWidget *caption = create_caption_new_with_asterisk (self, sizegroup, _("mcen_fi_account_title"), + priv->entry_account_title, NULL, HILDON_CAPTION_MANDATORY); + gtk_widget_show (priv->entry_account_title); + connect_for_modified (self, priv->entry_account_title); + gtk_box_pack_start (GTK_BOX (box), caption, FALSE, FALSE, MODEST_MARGIN_HALF); + gtk_widget_show (caption); + + /* Prevent the use of some characters in the account title, + * as required by our UI specification: */ + GList *list_prevent = NULL; + list_prevent = g_list_append (list_prevent, "\\"); + list_prevent = g_list_append (list_prevent, "/"); + list_prevent = g_list_append (list_prevent, ":"); + list_prevent = g_list_append (list_prevent, "*"); + list_prevent = g_list_append (list_prevent, "?"); + list_prevent = g_list_append (list_prevent, "\""); + list_prevent = g_list_append (list_prevent, "<"); + list_prevent = g_list_append (list_prevent, ">"); + list_prevent = g_list_append (list_prevent, "|"); + list_prevent = g_list_append (list_prevent, "^"); + modest_validating_entry_set_unallowed_characters ( + MODEST_VALIDATING_ENTRY (priv->entry_account_title), list_prevent); + g_list_free (list_prevent); + modest_validating_entry_set_func(MODEST_VALIDATING_ENTRY(priv->entry_account_title), + on_entry_invalid_account_title_character, self); + + /* Set max length as in the UI spec: + * The UI spec seems to want us to show a dialog if we hit the maximum. */ + gtk_entry_set_max_length (GTK_ENTRY (priv->entry_account_title), 64); + modest_validating_entry_set_max_func (MODEST_VALIDATING_ENTRY (priv->entry_account_title), + on_entry_max, self); + + /* The retrieve combobox: */ + priv->combo_retrieve = GTK_WIDGET (modest_retrieve_combo_box_new ()); + caption = create_caption_new_with_asterisk (self, sizegroup, _("mcen_fi_advsetup_retrievetype"), + priv->combo_retrieve, NULL, HILDON_CAPTION_MANDATORY); + gtk_widget_show (priv->combo_retrieve); + connect_for_modified (self, priv->combo_retrieve); + gtk_box_pack_start (GTK_BOX (box), caption, FALSE, FALSE, MODEST_MARGIN_HALF); + gtk_widget_show (caption); + + /* The limit-retrieve combobox: */ + priv->combo_limit_retrieve = GTK_WIDGET (modest_limit_retrieve_combo_box_new ()); + caption = create_caption_new_with_asterisk (self, sizegroup, _("mcen_fi_advsetup_limit_retrieve"), + priv->combo_limit_retrieve, NULL, HILDON_CAPTION_MANDATORY); + gtk_widget_show (priv->combo_limit_retrieve); + connect_for_modified (self, priv->combo_limit_retrieve); + gtk_box_pack_start (GTK_BOX (box), caption, FALSE, FALSE, MODEST_MARGIN_HALF); + gtk_widget_show (caption); + + /* The leave-messages widgets: */ + if(!priv->checkbox_leave_messages) + priv->checkbox_leave_messages = gtk_check_button_new (); + if (!priv->caption_leave_messages) { + priv->caption_leave_messages = create_caption_new_with_asterisk (self, sizegroup, _("mcen_fi_advsetup_leave_on_server"), + priv->checkbox_leave_messages, NULL, HILDON_CAPTION_MANDATORY); + } + + gtk_widget_show (priv->checkbox_leave_messages); + connect_for_modified (self, priv->checkbox_leave_messages); + gtk_box_pack_start (GTK_BOX (box), priv->caption_leave_messages, FALSE, FALSE, MODEST_MARGIN_HALF); + gtk_widget_show (priv->caption_leave_messages); + + g_object_unref (sizegroup); + + gtk_widget_show (GTK_WIDGET (box)); + + gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scrollwin), box); + gtk_widget_show (scrollwin); + + focus_adjustment = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (scrollwin)); + gtk_container_set_focus_vadjustment (GTK_CONTAINER (box), focus_adjustment); + + return GTK_WIDGET (scrollwin); +} + +static gchar* +get_entered_account_title (ModestDefaultAccountSettingsDialog *dialog) +{ + ModestDefaultAccountSettingsDialogPrivate *priv; + const gchar* account_title; + + priv = MODEST_DEFAULT_ACCOUNT_SETTINGS_DIALOG_GET_PRIVATE (dialog); + account_title = gtk_entry_get_text (GTK_ENTRY (priv->entry_account_title)); + + if (!account_title || (strlen (account_title) == 0)) + return NULL; + else { + /* Strip it of whitespace at the start and end: */ + gchar *result = g_strdup (account_title); + result = g_strstrip (result); + + if (!result) + return NULL; + + if (strlen (result) == 0) { + g_free (result); + return NULL; + } + + return result; + } +} + + +static void +on_button_signature (GtkButton *button, gpointer user_data) +{ + ModestDefaultAccountSettingsDialog *self; + gint response; + ModestDefaultAccountSettingsDialogPrivate *priv; + + self = MODEST_DEFAULT_ACCOUNT_SETTINGS_DIALOG (user_data); + priv = MODEST_DEFAULT_ACCOUNT_SETTINGS_DIALOG_GET_PRIVATE (self); + + /* Create the window, if necessary: */ + if (!(priv->signature_dialog)) { + priv->signature_dialog = GTK_WIDGET (modest_signature_editor_dialog_new ()); + + gboolean use_signature = modest_account_settings_get_use_signature (priv->settings); + const gchar *signature = modest_account_settings_get_signature(priv->settings); + gchar* account_title = get_entered_account_title (self); + modest_signature_editor_dialog_set_settings ( + MODEST_SIGNATURE_EDITOR_DIALOG (priv->signature_dialog), + use_signature, signature, account_title); + + g_free (account_title); + account_title = NULL; + signature = NULL; + } + + /* Show the window: */ + modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), + GTK_WINDOW (priv->signature_dialog)); + + response = gtk_dialog_run (GTK_DIALOG (priv->signature_dialog)); + gtk_widget_hide (priv->signature_dialog); + if (response != GTK_RESPONSE_OK) { + /* Destroy the widget now, and its data: */ + gtk_widget_destroy (priv->signature_dialog); + priv->signature_dialog = NULL; + } else { + /* Mark modified, so we use the dialog's data later: */ + priv->modified = TRUE; + } +} + +static GtkWidget* +create_page_user_details (ModestDefaultAccountSettingsDialog *self) +{ + ModestDefaultAccountSettingsDialogPrivate *priv; + GtkWidget *box; + GtkAdjustment *focus_adjustment = NULL; + GtkSizeGroup* sizegroup; + GtkWidget *scrollwin; + + priv = MODEST_DEFAULT_ACCOUNT_SETTINGS_DIALOG_GET_PRIVATE (self); + + box = gtk_vbox_new (FALSE, MODEST_MARGIN_NONE); + sizegroup = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); + scrollwin = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrollwin), + GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); + + /* The name widgets: */ + priv->entry_user_name = GTK_WIDGET (modest_validating_entry_new ()); + + /* Auto-capitalization is the default, so let's turn it off: */ + hildon_gtk_entry_set_input_mode (GTK_ENTRY (priv->entry_user_name), HILDON_GTK_INPUT_MODE_FULL); + /* Set max length as in the UI spec: + * The UI spec seems to want us to show a dialog if we hit the maximum. */ + gtk_entry_set_max_length (GTK_ENTRY (priv->entry_user_name), 64); + modest_validating_entry_set_max_func (MODEST_VALIDATING_ENTRY (priv->entry_user_name), + on_entry_max, self); + GtkWidget *caption = create_caption_new_with_asterisk (self, sizegroup, + _("mcen_li_emailsetup_name"), priv->entry_user_name, NULL, HILDON_CAPTION_OPTIONAL); + gtk_widget_show (priv->entry_user_name); + connect_for_modified (self, priv->entry_user_name); + gtk_box_pack_start (GTK_BOX (box), caption, FALSE, FALSE, MODEST_MARGIN_HALF); + gtk_widget_show (caption); + + + /* Prevent the use of some characters in the name, + * as required by our UI specification: */ + GList *list_prevent = NULL; + list_prevent = g_list_append (list_prevent, "<"); + list_prevent = g_list_append (list_prevent, ">"); + modest_validating_entry_set_unallowed_characters ( + MODEST_VALIDATING_ENTRY (priv->entry_user_name), list_prevent); + g_list_free (list_prevent); + modest_validating_entry_set_func(MODEST_VALIDATING_ENTRY(priv->entry_user_name), + on_entry_invalid_fullname_character, self); + + /* The username widgets: */ + priv->entry_user_username = GTK_WIDGET (modest_validating_entry_new ()); + /* Auto-capitalization is the default, so let's turn it off: */ + hildon_gtk_entry_set_input_mode (GTK_ENTRY (priv->entry_user_username), HILDON_GTK_INPUT_MODE_FULL); + caption = create_caption_new_with_asterisk (self, sizegroup, _("mail_fi_username"), + priv->entry_user_username, NULL, HILDON_CAPTION_MANDATORY); + gtk_widget_show (priv->entry_user_username); + connect_for_modified (self, priv->entry_user_username); + gtk_box_pack_start (GTK_BOX (box), caption, FALSE, FALSE, MODEST_MARGIN_HALF); + gtk_widget_show (caption); + + /* Prevent the use of some characters in the username, + * as required by our UI specification: */ + modest_validating_entry_set_unallowed_characters_whitespace ( + MODEST_VALIDATING_ENTRY (priv->entry_user_username)); + modest_validating_entry_set_func (MODEST_VALIDATING_ENTRY (priv->entry_user_username), + modest_utils_on_entry_invalid_character, + self); + + /* Set max length as in the UI spec: + * The UI spec seems to want us to show a dialog if we hit the maximum. */ + gtk_entry_set_max_length (GTK_ENTRY (priv->entry_user_username), 64); + modest_validating_entry_set_max_func (MODEST_VALIDATING_ENTRY (priv->entry_user_username), + on_entry_max, self); + + /* The password widgets: */ + priv->entry_user_password = gtk_entry_new (); + /* Auto-capitalization is the default, so let's turn it off: */ + hildon_gtk_entry_set_input_mode (GTK_ENTRY (priv->entry_user_password), + HILDON_GTK_INPUT_MODE_FULL | HILDON_GTK_INPUT_MODE_INVISIBLE); + gtk_entry_set_visibility (GTK_ENTRY (priv->entry_user_password), FALSE); + /* gtk_entry_set_invisible_char (GTK_ENTRY (priv->entry_user_password), '*'); */ + caption = create_caption_new_with_asterisk (self, sizegroup, + _("mail_fi_password"), priv->entry_user_password, NULL, HILDON_CAPTION_OPTIONAL); + gtk_widget_show (priv->entry_user_password); + connect_for_modified (self, priv->entry_user_password); + gtk_box_pack_start (GTK_BOX (box), caption, FALSE, FALSE, MODEST_MARGIN_HALF); + gtk_widget_show (caption); + + /* The email address widgets: */ + priv->entry_user_email = GTK_WIDGET (modest_validating_entry_new ()); + /* Auto-capitalization is the default, so let's turn it off: */ + hildon_gtk_entry_set_input_mode (GTK_ENTRY (priv->entry_user_email), HILDON_GTK_INPUT_MODE_FULL); + caption = create_caption_new_with_asterisk (self, sizegroup, + _("mcen_li_emailsetup_email_address"), priv->entry_user_email, NULL, HILDON_CAPTION_MANDATORY); + gtk_entry_set_text (GTK_ENTRY (priv->entry_user_email), MODEST_EXAMPLE_EMAIL_ADDRESS); /* Default text. */ + gtk_widget_show (priv->entry_user_email); + connect_for_modified (self, priv->entry_user_email); + gtk_box_pack_start (GTK_BOX (box), caption, FALSE, FALSE, MODEST_MARGIN_HALF); + gtk_widget_show (caption); + + /* Set max length as in the UI spec: + * The UI spec seems to want us to show a dialog if we hit the maximum. */ + gtk_entry_set_max_length (GTK_ENTRY (priv->entry_user_email), 64); + modest_validating_entry_set_max_func (MODEST_VALIDATING_ENTRY (priv->entry_user_email), + on_entry_max, self); + + /* Signature button: */ + if (!priv->button_signature) + priv->button_signature = gtk_button_new_with_label (_("mcen_bd_edit")); + caption = hildon_caption_new (sizegroup, _("mcen_fi_email_signature"), + priv->button_signature, NULL, HILDON_CAPTION_OPTIONAL); + hildon_caption_set_child_expand (HILDON_CAPTION (caption), FALSE); + gtk_widget_show (priv->button_signature); + gtk_box_pack_start (GTK_BOX (box), caption, FALSE, FALSE, MODEST_MARGIN_HALF); + gtk_widget_show (caption); + + g_object_unref (sizegroup); + + g_signal_connect (G_OBJECT (priv->button_signature), "clicked", + G_CALLBACK (on_button_signature), self); + + gtk_widget_show (GTK_WIDGET (box)); + gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scrollwin), box); + gtk_widget_show (scrollwin); + + focus_adjustment = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (scrollwin)); + gtk_container_set_focus_vadjustment (GTK_CONTAINER (box), focus_adjustment); + + return GTK_WIDGET (scrollwin); +} + +/* Change the caption title for the incoming server */ +static void +update_incoming_server_title (ModestDefaultAccountSettingsDialog *self, + ModestProtocolType protocol_type) +{ + ModestProtocolRegistry *protocol_registry; + ModestProtocol *protocol; + const gchar *protocol_display_name; + gchar* incomingserver_title; + gchar *with_asterisk; + ModestDefaultAccountSettingsDialogPrivate *priv; + + priv = MODEST_DEFAULT_ACCOUNT_SETTINGS_DIALOG_GET_PRIVATE (self); + + protocol_registry = modest_runtime_get_protocol_registry (); + protocol = modest_protocol_registry_get_protocol_by_type (protocol_registry, protocol_type); + protocol_display_name = modest_protocol_get_display_name (protocol); + incomingserver_title = g_strdup_printf(_("mcen_li_emailsetup_servertype"), + protocol_display_name); + + /* This is a mandatory field, so add a *. This is usually done by + * create_caption_new_with_asterisk() but we can't use that here. */ + with_asterisk = g_strconcat (incomingserver_title, "*", NULL); + g_free (incomingserver_title); + + g_object_set (G_OBJECT (priv->caption_incoming), "label", with_asterisk, NULL); + g_free (with_asterisk); +} + +/** Change the caption title for the incoming server, + * as specified in the UI spec: + */ +/* static void */ +/* update_incoming_server_security_choices (ModestDefaultAccountSettingsDialog *self, */ +/* ModestProtocolType protocol) */ +/* { */ +/* /\* Fill the combo with appropriately titled choices for POP or IMAP. *\/ */ +/* /\* The choices are the same, but the titles are different, as in the UI spec. *\/ */ +/* modest_serversecurity_combo_box_fill ( */ +/* MODEST_SERVERSECURITY_COMBO_BOX (priv->combo_incoming_security), protocol); */ +/* } */ + +static GtkWidget* +create_page_incoming (ModestDefaultAccountSettingsDialog *self) +{ + ModestDefaultAccountSettingsDialogPrivate *priv; + GtkWidget *box; + GtkSizeGroup *sizegroup; + + priv = MODEST_DEFAULT_ACCOUNT_SETTINGS_DIALOG_GET_PRIVATE (self); + + box = gtk_vbox_new (FALSE, MODEST_MARGIN_NONE); + /* Create a size group to be used by all captions. + * Note that HildonCaption does not create a default size group if we do not specify one. + * We use GTK_SIZE_GROUP_HORIZONTAL, so that the widths are the same. */ + sizegroup = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); + + /* The incoming server widgets: */ + if(!priv->entry_incomingserver) + priv->entry_incomingserver = gtk_entry_new (); + /* Auto-capitalization is the default, so let's turn it off: */ + hildon_gtk_entry_set_input_mode (GTK_ENTRY (priv->entry_incomingserver), HILDON_GTK_INPUT_MODE_FULL); + + if (priv->caption_incoming) + gtk_widget_destroy (priv->caption_incoming); + + /* The caption title will be updated in update_incoming_server_title(). + * so this default text will never be seen: */ + /* (Note: Changing the title seems pointless. murrayc) */ + priv->caption_incoming = create_caption_new_with_asterisk (self, sizegroup, + "Incoming Server", priv->entry_incomingserver, NULL, HILDON_CAPTION_MANDATORY); + gtk_widget_show (priv->entry_incomingserver); + connect_for_modified (self, priv->entry_incomingserver); + gtk_box_pack_start (GTK_BOX (box), priv->caption_incoming, FALSE, FALSE, MODEST_MARGIN_HALF); + gtk_widget_show (priv->caption_incoming); + + /* Incoming security widgets */ + priv->incoming_security = + modest_maemo_security_options_view_new (MODEST_SECURITY_OPTIONS_INCOMING, + TRUE, sizegroup); + gtk_box_pack_start (GTK_BOX (box), priv->incoming_security, + FALSE, FALSE, MODEST_MARGIN_HALF); + + gtk_widget_show (priv->incoming_security); + + g_object_unref (sizegroup); + gtk_widget_show (GTK_WIDGET (box)); + + return GTK_WIDGET (box); +} + +static void +on_toggle_button_changed (GtkToggleButton *togglebutton, gpointer user_data) +{ + GtkWidget *widget = GTK_WIDGET (user_data); + + /* Enable the widget only if the toggle button is active: */ + const gboolean enable = gtk_toggle_button_get_active (togglebutton); + gtk_widget_set_sensitive (widget, enable); +} + +/* Make the sensitivity of a widget depend on a toggle button. + */ +static void +enable_widget_for_togglebutton (GtkWidget *widget, GtkToggleButton* button) +{ + g_signal_connect (G_OBJECT (button), "toggled", + G_CALLBACK (on_toggle_button_changed), widget); + + /* Set the starting sensitivity: */ + on_toggle_button_changed (button, widget); +} + +static void +on_button_outgoing_smtp_servers (GtkButton *button, gpointer user_data) +{ + ModestDefaultAccountSettingsDialog * self = MODEST_DEFAULT_ACCOUNT_SETTINGS_DIALOG (user_data); + ModestConnectionSpecificSmtpWindow *smtp_win; + ModestDefaultAccountSettingsDialogPrivate *priv; + + priv = MODEST_DEFAULT_ACCOUNT_SETTINGS_DIALOG_GET_PRIVATE (self); + + /* Create the window if necessary: */ + smtp_win = modest_connection_specific_smtp_window_new (); + modest_connection_specific_smtp_window_fill_with_connections (smtp_win, priv->account_manager); + + /* Show the window: */ + modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), GTK_WINDOW (smtp_win)); + gtk_widget_show (GTK_WIDGET (smtp_win)); + priv->modified = TRUE; +} + +/* static void */ +/* on_combo_outgoing_auth_changed (GtkComboBox *widget, gpointer user_data) */ +/* { */ +/* ModestDefaultAccountSettingsDialog *self; */ +/* ModestProtocolRegistry *protocol_registry; */ +/* ModestProtocolType protocol_security; */ +/* gboolean secureauth_used; */ + +/* self = MODEST_DEFAULT_ACCOUNT_SETTINGS_DIALOG (user_data); */ +/* protocol_registry = modest_runtime_get_protocol_registry (); */ + +/* protocol_security = */ +/* modest_secureauth_combo_box_get_active_secureauth ( */ +/* MODEST_SECUREAUTH_COMBO_BOX (priv->combo_outgoing_auth)); */ +/* secureauth_used = modest_protocol_registry_protocol_type_is_secure (protocol_registry, protocol_security); */ + +/* gtk_widget_set_sensitive (priv->caption_outgoing_username, secureauth_used); */ +/* gtk_widget_set_sensitive (priv->caption_outgoing_password, secureauth_used); */ +/* } */ + +/* static void */ +/* on_combo_outgoing_security_changed (GtkComboBox *widget, gpointer user_data) */ +/* { */ +/* ModestDefaultAccountSettingsDialog *self = MODEST_DEFAULT_ACCOUNT_SETTINGS_DIALOG (user_data); */ + +/* const gint port_number = */ +/* modest_serversecurity_combo_box_get_active_serversecurity_port ( */ +/* MODEST_SERVERSECURITY_COMBO_BOX (priv->combo_outgoing_security)); */ + +/* if(port_number != 0) { */ +/* hildon_number_editor_set_value ( */ +/* HILDON_NUMBER_EDITOR (priv->entry_outgoing_port), port_number); */ +/* } */ +/* } */ + +static void +on_missing_mandatory_data (ModestSecurityOptionsView *security_view, + gboolean missing, + gpointer user_data) +{ + /* Disable the OK button */ + gtk_dialog_set_response_sensitive (GTK_DIALOG (user_data), + GTK_RESPONSE_OK, + !missing); +} + +static GtkWidget* +create_page_outgoing (ModestDefaultAccountSettingsDialog *self) +{ + ModestDefaultAccountSettingsDialogPrivate *priv; + GtkWidget *box = gtk_vbox_new (FALSE, MODEST_MARGIN_NONE); + GtkAdjustment *focus_adjustment = NULL; + + priv = MODEST_DEFAULT_ACCOUNT_SETTINGS_DIALOG_GET_PRIVATE (self); + + /* Put it all in a scrolled window, so that all widgets can be + * accessed even when the on-screen keyboard is visible: */ + GtkWidget *scrollwin = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollwin), + GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); + + /* Create a size group to be used by all captions. + * Note that HildonCaption does not create a default size group if we do not specify one. + * We use GTK_SIZE_GROUP_HORIZONTAL, so that the widths are the same. */ + GtkSizeGroup *sizegroup = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); + + /* The outgoing server widgets: */ + if (!priv->entry_outgoingserver) + priv->entry_outgoingserver = gtk_entry_new (); + /* Auto-capitalization is the default, so let's turn it off: */ + hildon_gtk_entry_set_input_mode (GTK_ENTRY (priv->entry_outgoingserver), HILDON_GTK_INPUT_MODE_FULL); + GtkWidget *caption = create_caption_new_with_asterisk (self, sizegroup, + _("mcen_li_emailsetup_smtp"), priv->entry_outgoingserver, NULL, HILDON_CAPTION_OPTIONAL); + gtk_widget_show (priv->entry_outgoingserver); + connect_for_modified (self, priv->entry_outgoingserver); + gtk_box_pack_start (GTK_BOX (box), caption, FALSE, FALSE, MODEST_MARGIN_HALF); + gtk_widget_show (caption); + + /* Outgoing security widgets */ + priv->outgoing_security = + modest_maemo_security_options_view_new (MODEST_SECURITY_OPTIONS_OUTGOING, + TRUE, sizegroup); + gtk_box_pack_start (GTK_BOX (box), priv->outgoing_security, + FALSE, FALSE, MODEST_MARGIN_HALF); + gtk_widget_show (priv->outgoing_security); + g_signal_connect (priv->outgoing_security, "missing-mandatory-data", + G_CALLBACK (on_missing_mandatory_data), self); + + GtkWidget *separator = gtk_hseparator_new (); + gtk_box_pack_start (GTK_BOX (box), separator, FALSE, FALSE, MODEST_MARGIN_HALF); + gtk_widget_show (separator); + + /* connection-specific checkbox: */ + if (!priv->checkbox_outgoing_smtp_specific) { + priv->checkbox_outgoing_smtp_specific = gtk_check_button_new (); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->checkbox_outgoing_smtp_specific), + FALSE); + } + caption = hildon_caption_new (sizegroup, _("mcen_fi_advsetup_connection_smtp"), + priv->checkbox_outgoing_smtp_specific, NULL, HILDON_CAPTION_OPTIONAL); + gtk_widget_show (priv->checkbox_outgoing_smtp_specific); + gtk_box_pack_start (GTK_BOX (box), caption, FALSE, FALSE, MODEST_MARGIN_HALF); + gtk_widget_show (caption); + connect_for_modified (self, priv->checkbox_outgoing_smtp_specific); + + /* Connection-specific SMTP-Severs Edit button: */ + if (!priv->button_outgoing_smtp_servers) + priv->button_outgoing_smtp_servers = gtk_button_new_with_label (_("mcen_bd_edit")); + caption = hildon_caption_new (sizegroup, _("mcen_fi_advsetup_optional_smtp"), + priv->button_outgoing_smtp_servers, NULL, HILDON_CAPTION_OPTIONAL); + hildon_caption_set_child_expand (HILDON_CAPTION (caption), FALSE); + gtk_widget_show (priv->button_outgoing_smtp_servers); + gtk_box_pack_start (GTK_BOX (box), caption, FALSE, FALSE, MODEST_MARGIN_HALF); + gtk_widget_show (caption); + + /* Only enable the button when the checkbox is checked: */ + enable_widget_for_togglebutton (priv->button_outgoing_smtp_servers, + GTK_TOGGLE_BUTTON (priv->checkbox_outgoing_smtp_specific)); + + g_object_unref (sizegroup); + + g_signal_connect (G_OBJECT (priv->button_outgoing_smtp_servers), "clicked", + G_CALLBACK (on_button_outgoing_smtp_servers), self); + + gtk_widget_show (GTK_WIDGET (box)); + gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW(scrollwin), box); + gtk_widget_show(scrollwin); + + focus_adjustment = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (scrollwin)); + gtk_container_set_focus_vadjustment (GTK_CONTAINER (box), focus_adjustment); + + return GTK_WIDGET (scrollwin); +} + +static gboolean +check_data (ModestDefaultAccountSettingsDialog *self) +{ + gchar* account_title; + const gchar* email_address; + const gchar* hostname; + const gchar* hostname2; + ModestDefaultAccountSettingsDialogPrivate *priv; + + priv = MODEST_DEFAULT_ACCOUNT_SETTINGS_DIALOG_GET_PRIVATE (self); + + /* Check that the title is not already in use: */ + account_title = get_entered_account_title (self); + if (!account_title) + return FALSE; /* Should be prevented already anyway. */ + + if (strcmp(account_title, priv->original_account_title) != 0) { + gboolean name_in_use; + + /* Check the changed title: */ + name_in_use = modest_account_mgr_account_with_display_name_exists (priv->account_manager, + account_title); + + if (name_in_use) { + /* Warn the user via a dialog: */ + modest_platform_information_banner(NULL, NULL, _("mail_ib_account_name_already_existing")); + + g_free (account_title); + return FALSE; + } + } + + g_free (account_title); + account_title = NULL; + + /* Check that the email address is valid: */ + email_address = gtk_entry_get_text (GTK_ENTRY (priv->entry_user_email)); + if ((!email_address) || (strlen(email_address) == 0)) { + return FALSE; + } + + if (!modest_text_utils_validate_email_address (email_address, NULL)) { + /* Warn the user via a dialog: */ + modest_platform_information_banner (NULL, NULL, _("mcen_ib_invalid_email")); + + /* Return focus to the email address entry: */ + gtk_widget_grab_focus (priv->entry_user_email); + gtk_editable_select_region (GTK_EDITABLE (priv->entry_user_email), 0, -1); + return FALSE; + } + + /* make sure the domain name for the incoming server is valid */ + hostname = gtk_entry_get_text (GTK_ENTRY (priv->entry_incomingserver)); + if ((!hostname) || (strlen(hostname) == 0)) { + return FALSE; + } + + if (!modest_text_utils_validate_domain_name (hostname)) { + /* Warn the user via a dialog: */ + modest_platform_information_banner (NULL, NULL, _("mcen_ib_invalid_servername")); + + /* Return focus to the email address entry: */ + gtk_widget_grab_focus (priv->entry_incomingserver); + gtk_editable_select_region (GTK_EDITABLE (priv->entry_incomingserver), 0, -1); + return FALSE; + } + + /* make sure the domain name for the outgoing server is valid */ + hostname2 = gtk_entry_get_text (GTK_ENTRY (priv->entry_outgoingserver)); + if ((!hostname2) || (strlen(hostname2) == 0)) { + return FALSE; + } + + if (!modest_text_utils_validate_domain_name (hostname2)) { + /* Warn the user via a dialog: */ + modest_platform_information_banner (priv->entry_outgoingserver, NULL, _("mcen_ib_invalid_servername")); + + /* Return focus to the email address entry: */ + gtk_widget_grab_focus (priv->entry_outgoingserver); + gtk_editable_select_region (GTK_EDITABLE (priv->entry_outgoingserver), 0, -1); + return FALSE; + } + +/* /\* Find a suitable authentication method when secure authentication is desired *\/ */ +/* port_num = hildon_number_editor_get_value ( */ +/* HILDON_NUMBER_EDITOR (priv->entry_incoming_port)); */ +/* username = gtk_entry_get_text (GTK_ENTRY (priv->entry_user_username)); */ + +/* protocol_registry = modest_runtime_get_protocol_registry (); */ + +/* protocol_security_incoming = modest_serversecurity_combo_box_get_active_serversecurity ( */ +/* MODEST_SERVERSECURITY_COMBO_BOX (priv->combo_incoming_security)); */ +/* if (!modest_protocol_registry_protocol_type_is_secure(protocol_registry, protocol_security_incoming)) */ +/* { */ +/* if (gtk_toggle_button_get_active ( */ +/* GTK_TOGGLE_BUTTON (priv->checkbox_incoming_auth))) { */ +/* GError *error = NULL; */ +/* GList *list_auth_methods; */ + +/* list_auth_methods = */ +/* modest_utils_get_supported_secure_authentication_methods (priv->incoming_protocol, */ +/* hostname, port_num, username, GTK_WINDOW (self), &error); */ +/* if (list_auth_methods) { */ +/* GList* method; */ + +/* /\* Use the first supported method. */ +/* * TODO: Should we prioritize them, to prefer a particular one? *\/ */ +/* for (method = list_auth_methods; method != NULL; method = g_list_next(method)) */ +/* { */ +/* ModestProtocolType proto = (ModestProtocolType)(GPOINTER_TO_INT(method->data)); */ +/* // Allow secure methods, e.g MD5 only */ +/* if (modest_protocol_registry_protocol_type_is_secure(protocol_registry, proto)) */ +/* { */ +/* priv->protocol_authentication_incoming = proto; */ +/* break; */ +/* } */ +/* } */ +/* g_list_free (list_auth_methods); */ +/* } */ + +/* if (list_auth_methods == NULL || */ +/* !modest_protocol_registry_protocol_type_is_secure(protocol_registry, priv->protocol_authentication_incoming)) */ +/* { */ +/* if(error == NULL || error->domain != modest_utils_get_supported_secure_authentication_error_quark() || */ +/* error->code != MODEST_UTILS_GET_SUPPORTED_SECURE_AUTHENTICATION_ERROR_CANCELED) */ +/* modest_platform_information_banner(GTK_WIDGET (self), NULL, */ +/* _("mcen_ib_unableto_discover_auth_methods")); */ + +/* if(error != NULL) */ +/* g_error_free(error); */ + +/* /\* This is a nasty hack. jschmid. *\/ */ +/* /\* Don't let the dialog close *\/ */ +/* /\*g_signal_stop_emission_by_name (dialog, "response");*\/ */ +/* return FALSE; */ +/* } */ +/* } */ +/* } */ + + return TRUE; +} + +static void +on_response (GtkDialog *wizard_dialog, + gint response_id, + gpointer user_data) +{ + ModestDefaultAccountSettingsDialog *self = MODEST_DEFAULT_ACCOUNT_SETTINGS_DIALOG (wizard_dialog); + ModestDefaultAccountSettingsDialogPrivate *priv; + gboolean prevent_response = FALSE, sec_changed; + ModestSecurityOptionsView *incoming_sec, *outgoing_sec; + + priv = MODEST_DEFAULT_ACCOUNT_SETTINGS_DIALOG_GET_PRIVATE (self); + enable_buttons (self); + + /* Check if security widgets changed */ + incoming_sec = MODEST_SECURITY_OPTIONS_VIEW (priv->incoming_security); + outgoing_sec = MODEST_SECURITY_OPTIONS_VIEW (priv->outgoing_security); + sec_changed = + modest_security_options_view_changed (incoming_sec, priv->settings) || + modest_security_options_view_changed (outgoing_sec, priv->settings); + + /* Warn about unsaved changes: */ + if (response_id == GTK_RESPONSE_CANCEL && (priv->modified || sec_changed)) { + GtkDialog *dialog = GTK_DIALOG (hildon_note_new_confirmation (GTK_WINDOW (self), + _("imum_nc_wizard_confirm_lose_changes"))); + /* TODO: These button names will be ambiguous, and not specified in the UI specification. */ + + const gint dialog_response = gtk_dialog_run (dialog); + gtk_widget_destroy (GTK_WIDGET (dialog)); + + if (dialog_response != GTK_RESPONSE_OK) + prevent_response = TRUE; + } + /* Check for invalid input: */ + else if (response_id != GTK_RESPONSE_CANCEL && !check_data (self)) { + prevent_response = TRUE; + } + + if (prevent_response) { + /* This is a nasty hack. murrayc. */ + /* Don't let the dialog close */ + g_signal_stop_emission_by_name (wizard_dialog, "response"); + return; + } else { + modest_tny_account_store_set_send_mail_blocked (modest_runtime_get_account_store (), FALSE); + } + + if (response_id == GTK_RESPONSE_OK) { + /* Try to save the changes if modified (NB #59251): */ + if (priv->modified || sec_changed) { + if (save_configuration (self)) { + /* Do not show the account-saved dialog if we are just saving this + * temporarily, because from the user's point of view it will not + * really be saved (saved + enabled) until later + */ + if (modest_account_settings_get_account_name (priv->settings) != NULL) { + ModestServerAccountSettings *store_settings; + ModestServerAccountSettings *transport_settings; + const gchar *store_account_name; + const gchar *transport_account_name; + + + store_settings = modest_account_settings_get_store_settings (priv->settings); + transport_settings = modest_account_settings_get_transport_settings (priv->settings); + store_account_name = modest_server_account_settings_get_account_name (store_settings); + transport_account_name = modest_server_account_settings_get_account_name (transport_settings); + + if (store_account_name) { + modest_account_mgr_notify_account_update (priv->account_manager, + store_account_name); + } + if (transport_account_name) { + modest_account_mgr_notify_account_update (priv->account_manager, + transport_account_name); + } + g_object_unref (store_settings); + g_object_unref (transport_settings); + + modest_platform_information_banner(NULL, NULL, _("mcen_ib_advsetup_settings_saved")); + } + } else { + modest_platform_information_banner (NULL, NULL, _("mail_ib_setting_failed")); + } + } + } +} + +static void +modest_default_account_settings_dialog_init (ModestDefaultAccountSettingsDialog *self) +{ + ModestDefaultAccountSettingsDialogPrivate *priv; + + priv = MODEST_DEFAULT_ACCOUNT_SETTINGS_DIALOG_GET_PRIVATE(self); + + priv->incoming_security = NULL; + priv->outgoing_security = NULL; + + /* Create the notebook to be used by the GtkDialog base class: + * Each page of the notebook will be a page of the wizard: */ + priv->notebook = GTK_NOTEBOOK (gtk_notebook_new()); + priv->settings = modest_account_settings_new (); + + /* Get the account manager object, + * so we can check for existing accounts, + * and create new accounts: */ + priv->account_manager = modest_runtime_get_account_mgr (); + g_assert (priv->account_manager); + g_object_ref (priv->account_manager); + + priv->protocol_authentication_incoming = MODEST_PROTOCOLS_AUTH_PASSWORD; + + /* Create the common pages, + */ + priv->page_account_details = create_page_account_details (self); + priv->page_user_details = create_page_user_details (self); + priv->page_incoming = create_page_incoming (self); + priv->page_outgoing = create_page_outgoing (self); + + /* Add the notebook pages: */ + gtk_notebook_append_page (priv->notebook, priv->page_account_details, + gtk_label_new (_("mcen_ti_account_settings_account"))); + gtk_notebook_append_page (priv->notebook, priv->page_user_details, + gtk_label_new (_("mcen_ti_account_settings_userinfo"))); + gtk_notebook_append_page (priv->notebook, priv->page_incoming, + gtk_label_new (_("mcen_ti_advsetup_retrieval"))); + gtk_notebook_append_page (priv->notebook, priv->page_outgoing, + gtk_label_new (_("mcen_ti_advsetup_sending"))); + + GtkDialog *dialog = GTK_DIALOG (self); + gtk_container_add (GTK_CONTAINER (dialog->vbox), GTK_WIDGET (priv->notebook)); + gtk_container_set_border_width (GTK_CONTAINER (dialog->vbox), MODEST_MARGIN_HALF); + gtk_widget_show (GTK_WIDGET (priv->notebook)); + + /* Add the buttons: */ + gtk_dialog_add_button (GTK_DIALOG(self), _("mcen_bd_dialog_ok"), GTK_RESPONSE_OK); + gtk_dialog_add_button (GTK_DIALOG(self), _("mcen_bd_dialog_cancel"), GTK_RESPONSE_CANCEL); + + /* Connect to the dialog's response signal: */ + /* We use connect-before + * so we can stop the signal emission, + * to stop the default signal handler from closing the dialog. + */ + g_signal_connect (G_OBJECT (self), "response", + G_CALLBACK (on_response), self); + + priv->modified = FALSE; + + /* When this window is shown, hibernation should not be possible, + * because there is no sensible way to save the state: */ + modest_window_mgr_prevent_hibernation_while_window_is_shown ( + modest_runtime_get_window_mgr (), GTK_WINDOW (self)); + + /* Prevent sending mails while editing an account, to avoid hangs on unprotected locks + * while sending messages causes an error dialog and we have a lock */ + modest_tny_account_store_set_send_mail_blocked (modest_runtime_get_account_store (), TRUE); + + hildon_help_dialog_help_enable (GTK_DIALOG(self), "applications_email_accountsettings", + modest_maemo_utils_get_osso_context()); +} + +ModestAccountSettingsDialog* +modest_default_account_settings_dialog_new (void) +{ + return g_object_new (MODEST_TYPE_DEFAULT_ACCOUNT_SETTINGS_DIALOG, NULL); +} + +/** Update the UI with the stored account details, so they can be edited. + * @account_name: Name of the account, which should contain incoming and outgoing server accounts. + */ +static void +modest_default_account_settings_dialog_load_settings (ModestAccountSettingsDialog *dialog, + ModestAccountSettings *settings) +{ + ModestServerAccountSettings *incoming_account; + ModestServerAccountSettings *outgoing_account; + ModestProtocolRegistry *protocol_registry; + const gchar *account_name, *server_account_name; + ModestDefaultAccountSettingsDialogPrivate *priv; + gint page_num; + gboolean username_known; + + g_return_if_fail (MODEST_IS_ACCOUNT_SETTINGS_DIALOG (dialog)); + g_return_if_fail (MODEST_IS_ACCOUNT_SETTINGS (settings)); + + priv = MODEST_DEFAULT_ACCOUNT_SETTINGS_DIALOG_GET_PRIVATE (dialog); + protocol_registry = modest_runtime_get_protocol_registry (); + + incoming_account = modest_account_settings_get_store_settings (settings); + outgoing_account = modest_account_settings_get_transport_settings (settings); + + account_name = modest_account_settings_get_account_name (settings); + + /* Save the account name so we can refer to it later: */ + if (priv->account_name) + g_free (priv->account_name); + priv->account_name = g_strdup (account_name); + + if (priv->settings) + g_object_unref (priv->settings); + priv->settings = g_object_ref (settings); + + /* Save the account title so we can refer to it if the user changes it: */ + if (priv->original_account_title) + g_free (priv->original_account_title); + priv->original_account_title = g_strdup (modest_account_settings_get_display_name (settings)); + + /* Show the account data in the widgets: */ + + /* Note that we never show the non-display name in the UI. + * (Though the display name defaults to the non-display name at the start.) */ + gtk_entry_set_text( GTK_ENTRY (priv->entry_account_title), + null_means_empty (modest_account_settings_get_display_name (settings))); + gtk_entry_set_text( GTK_ENTRY (priv->entry_user_name), + null_means_empty (modest_account_settings_get_fullname (settings))); + gtk_entry_set_text( GTK_ENTRY (priv->entry_user_email), + null_means_empty (modest_account_settings_get_email_address (settings))); + modest_retrieve_combo_box_set_active_retrieve_conf (MODEST_RETRIEVE_COMBO_BOX (priv->combo_retrieve), + modest_account_settings_get_retrieve_type (settings)); + modest_limit_retrieve_combo_box_set_active_limit_retrieve ( + MODEST_LIMIT_RETRIEVE_COMBO_BOX (priv->combo_limit_retrieve), + modest_account_settings_get_retrieve_limit (settings)); + + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->checkbox_leave_messages), + modest_account_settings_get_leave_messages_on_server (settings)); + + + if (incoming_account) { + const gchar *username, *password, *hostname, *proto_str, *account_title; + gchar *proto_name, *title; + ModestProtocolType incoming_protocol; + + modest_retrieve_combo_box_fill (MODEST_RETRIEVE_COMBO_BOX (priv->combo_retrieve), modest_server_account_settings_get_protocol (incoming_account)); + + if (!modest_protocol_registry_protocol_type_has_leave_on_server (protocol_registry, + modest_server_account_settings_get_protocol (incoming_account))) { + gtk_widget_hide (priv->caption_leave_messages); + } else { + gtk_widget_show (priv->caption_leave_messages); + } + + /* Remember this for later: */ + incoming_protocol = modest_server_account_settings_get_protocol (incoming_account);; + + hostname = modest_server_account_settings_get_hostname (incoming_account); + username = modest_server_account_settings_get_username (incoming_account); + password = modest_server_account_settings_get_password (incoming_account); + gtk_entry_set_text( GTK_ENTRY (priv->entry_user_username), + null_means_empty (username)); + gtk_entry_set_text( GTK_ENTRY (priv->entry_user_password), + null_means_empty (password)); + + gtk_entry_set_text( GTK_ENTRY (priv->entry_incomingserver), + null_means_empty (hostname)); + + /* Load security settings */ + modest_security_options_view_load_settings ( + MODEST_SECURITY_OPTIONS_VIEW (priv->incoming_security), + settings); + gtk_widget_show (priv->incoming_security); + + /* Update the incoming label */ + update_incoming_server_title (MODEST_DEFAULT_ACCOUNT_SETTINGS_DIALOG (dialog), + incoming_protocol); + + /* Set window title according to account */ + proto_str = modest_protocol_get_display_name (modest_protocol_registry_get_protocol_by_type (protocol_registry, incoming_protocol)); + proto_name = g_utf8_strup (proto_str, -1); + account_title = modest_account_settings_get_display_name(settings); + title = g_strdup_printf(_("mcen_ti_account_settings"), proto_name, account_title); + + gtk_window_set_title (GTK_WINDOW (dialog), title); + + g_free (proto_name); + g_free (title); + g_object_unref (incoming_account); + } + + outgoing_account = modest_account_settings_get_transport_settings (settings); + if (outgoing_account) { + const gchar *hostname; + const gchar *username; + const gchar *password; + ModestProtocolType outgoing_protocol; + + /* Remember this for later: */ + outgoing_protocol = + modest_server_account_settings_get_protocol (outgoing_account); + + hostname = modest_server_account_settings_get_hostname (outgoing_account); + username = modest_server_account_settings_get_username (outgoing_account); + password = modest_server_account_settings_get_password (outgoing_account); + gtk_entry_set_text( GTK_ENTRY (priv->entry_outgoingserver), + null_means_empty (hostname)); + + /* Load security settings */ + modest_security_options_view_load_settings ( + MODEST_SECURITY_OPTIONS_VIEW (priv->outgoing_security), + settings); + gtk_widget_show (priv->outgoing_security); + + const gboolean has_specific = + modest_account_settings_get_use_connection_specific_smtp (settings); + gtk_toggle_button_set_active ( + GTK_TOGGLE_BUTTON (priv->checkbox_outgoing_smtp_specific), + has_specific); + g_object_unref (outgoing_account); + } + + /* Switch to user page */ + page_num = gtk_notebook_page_num (priv->notebook,priv->page_user_details); + gtk_notebook_set_current_page (priv->notebook, page_num); + + /* Check if we allow changes or not */ + server_account_name = modest_server_account_settings_get_account_name (incoming_account); + username_known = + modest_account_mgr_get_server_account_username_has_succeeded (priv->account_manager, + server_account_name); + gtk_widget_set_sensitive (priv->entry_user_username, !username_known); + gtk_widget_set_sensitive (priv->entry_incomingserver, !username_known); + modest_security_options_view_enable_changes (MODEST_SECURITY_OPTIONS_VIEW (priv->incoming_security), + !username_known); + + + /* Unset the modified flag so we can detect changes later: */ + priv->modified = FALSE; +} + +static gboolean +save_configuration (ModestDefaultAccountSettingsDialog *dialog) +{ + const gchar* user_fullname; + const gchar* emailaddress; + ModestServerAccountSettings *store_settings; + ModestServerAccountSettings *transport_settings; + ModestAccountRetrieveType retrieve_type; + gint retrieve_limit; + gboolean leave_on_server; + const gchar* hostname; + const gchar* username; + const gchar* password; + gchar* account_title; + ModestDefaultAccountSettingsDialogPrivate *priv; + const gchar* account_name; + + priv = MODEST_DEFAULT_ACCOUNT_SETTINGS_DIALOG_GET_PRIVATE (dialog); + account_name = priv->account_name; + + /* Set the account data from the widgets: */ + user_fullname = gtk_entry_get_text (GTK_ENTRY (priv->entry_user_name)); + modest_account_settings_set_fullname (priv->settings, user_fullname); + + emailaddress = gtk_entry_get_text (GTK_ENTRY (priv->entry_user_email)); + modest_account_settings_set_email_address (priv->settings, emailaddress); + + /* Signature: */ + if (priv->signature_dialog) { + gboolean use_signature = FALSE; + gchar *signature; + signature = modest_signature_editor_dialog_get_settings (MODEST_SIGNATURE_EDITOR_DIALOG (priv->signature_dialog), + &use_signature); + + modest_account_settings_set_use_signature (priv->settings, use_signature); + modest_account_settings_set_signature (priv->settings, signature); + } + + retrieve_type = modest_retrieve_combo_box_get_active_retrieve_conf ( + MODEST_RETRIEVE_COMBO_BOX (priv->combo_retrieve)); + modest_account_settings_set_retrieve_type (priv->settings, retrieve_type); + + retrieve_limit = modest_limit_retrieve_combo_box_get_active_limit_retrieve ( + MODEST_LIMIT_RETRIEVE_COMBO_BOX (priv->combo_limit_retrieve)); + modest_account_settings_set_retrieve_limit (priv->settings, retrieve_limit); + + leave_on_server = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->checkbox_leave_messages)); + modest_account_settings_set_leave_messages_on_server (priv->settings, leave_on_server); + + store_settings = modest_account_settings_get_store_settings (priv->settings); + + hostname = gtk_entry_get_text (GTK_ENTRY (priv->entry_incomingserver)); + modest_server_account_settings_set_hostname (store_settings, hostname); + + username = gtk_entry_get_text (GTK_ENTRY (priv->entry_user_username)); + modest_server_account_settings_set_username (store_settings, username); + + password = gtk_entry_get_text (GTK_ENTRY (priv->entry_user_password)); + modest_server_account_settings_set_password (store_settings, password); + + /* Save security settings */ + modest_security_options_view_save_settings (MODEST_SECURITY_OPTIONS_VIEW (priv->incoming_security), + priv->settings); + + g_object_unref (store_settings); + + /* Outgoing: */ + transport_settings = modest_account_settings_get_transport_settings (priv->settings); + + hostname = gtk_entry_get_text (GTK_ENTRY (priv->entry_outgoingserver)); + modest_server_account_settings_set_hostname (transport_settings, hostname); + + /* Save security settings */ + modest_security_options_view_save_settings ( + MODEST_SECURITY_OPTIONS_VIEW (priv->outgoing_security), + priv->settings); + + /* Set the changed account title last, to simplify the previous code: */ + account_title = get_entered_account_title (dialog); + if (!account_title) + return FALSE; /* Should be prevented already anyway. */ + +/* if (strcmp (account_title, account_name) != 0) { */ + modest_account_settings_set_display_name (priv->settings, account_title); +/* } */ + g_free (account_title); + account_title = NULL; + + /* Save connection-specific SMTP server accounts: */ + modest_account_settings_set_use_connection_specific_smtp + (priv->settings, + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->checkbox_outgoing_smtp_specific))); + + /* this configuration is not persistent, we should not save */ + if (account_name != NULL) + modest_account_mgr_save_account_settings (priv->account_manager, priv->settings); + + return TRUE; +} + +static gboolean entry_is_empty (GtkWidget *entry) +{ + if (!entry) + return FALSE; + + const gchar* text = gtk_entry_get_text (GTK_ENTRY (entry)); + if ((!text) || (strlen(text) == 0)) + return TRUE; + else { + /* Strip it of whitespace at the start and end: */ + gchar *stripped = g_strdup (text); + stripped = g_strstrip (stripped); + + if (!stripped) + return TRUE; + + const gboolean result = (strlen (stripped) == 0); + + g_free (stripped); + return result; + } +} + +static void +enable_buttons (ModestDefaultAccountSettingsDialog *self) +{ + gboolean enable_ok = TRUE; + ModestProtocolRegistry *protocol_registry; + ModestDefaultAccountSettingsDialogPrivate *priv; + + priv = MODEST_DEFAULT_ACCOUNT_SETTINGS_DIALOG_GET_PRIVATE (self); + + /* The account details title is mandatory: */ + if (entry_is_empty(priv->entry_account_title)) + enable_ok = FALSE; + + /* The user details username is mandatory: */ + if (enable_ok && entry_is_empty(priv->entry_user_username)) + enable_ok = FALSE; + + /* The user details email address is mandatory: */ + if (enable_ok && entry_is_empty (priv->entry_user_email)) + enable_ok = FALSE; + + /* The custom incoming server is mandatory: */ + if (enable_ok && entry_is_empty(priv->entry_incomingserver)) + enable_ok = FALSE; + + /* The custom outgoing server is mandatory: */ + if (enable_ok && entry_is_empty(priv->entry_outgoingserver)) + enable_ok = FALSE; + + protocol_registry = modest_runtime_get_protocol_registry (); + + /* Enable the buttons, + * identifying them via their associated response codes: + */ + GtkDialog *dialog_base = GTK_DIALOG (self); + gtk_dialog_set_response_sensitive (dialog_base, + GTK_RESPONSE_OK, + enable_ok); +} + +static void +modest_default_account_settings_dialog_class_init (ModestDefaultAccountSettingsDialogClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + g_type_class_add_private (klass, sizeof (ModestDefaultAccountSettingsDialogPrivate)); + + object_class->dispose = modest_default_account_settings_dialog_dispose; + object_class->finalize = modest_default_account_settings_dialog_finalize; +} + +static void +modest_account_settings_dialog_init (gpointer g_iface, gpointer iface_data) +{ + ModestAccountSettingsDialogClass *iface = (ModestAccountSettingsDialogClass *) g_iface; + + iface->load_settings = modest_default_account_settings_dialog_load_settings; +} diff --git a/src/hildon2/modest-easysetup-country-combo-box.c b/src/hildon2/modest-easysetup-country-combo-box.c new file mode 100644 index 0000000..683c365 --- /dev/null +++ b/src/hildon2/modest-easysetup-country-combo-box.c @@ -0,0 +1,394 @@ +/* Copyright (c) 2006, Nokia Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Nokia Corporation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE /* So we can use the getline() function, which is a convenient GNU extension. */ +#endif + +#include + +#include "modest-maemo-utils.h" +#include "modest-easysetup-country-combo-box.h" +#include +#include +#include + +#include +#include /* For memcpy() */ +#include +#include +#include /* For dgettext(). */ + +/* Include config.h so that _() works: */ +#ifdef HAVE_CONFIG_H +#include +#endif + +#define MAX_LINE_LEN 128 /* max length of a line in MCC file */ + +#if MODEST_HILDON_API < 2 +G_DEFINE_TYPE (EasysetupCountryComboBox, easysetup_country_combo_box, GTK_TYPE_COMBO_BOX); +#else +G_DEFINE_TYPE (EasysetupCountryComboBox, easysetup_country_combo_box, HILDON_TYPE_PICKER_BUTTON); +#endif + +typedef struct +{ + gint locale_mcc; +/* GtkTreeModel *model; */ +} ModestEasysetupCountryComboBoxPrivate; + +#define MODEST_EASYSETUP_COUNTRY_COMBO_BOX_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \ + MODEST_EASYSETUP_TYPE_COUNTRY_COMBO_BOX, \ + ModestEasysetupCountryComboBoxPrivate)) + +static void +easysetup_country_combo_box_get_property (GObject *object, guint property_id, + GValue *value, GParamSpec *pspec) +{ + switch (property_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +easysetup_country_combo_box_set_property (GObject *object, guint property_id, + const GValue *value, GParamSpec *pspec) +{ + switch (property_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +easysetup_country_combo_box_dispose (GObject *object) +{ + if (G_OBJECT_CLASS (easysetup_country_combo_box_parent_class)->dispose) + G_OBJECT_CLASS (easysetup_country_combo_box_parent_class)->dispose (object); +} + +enum MODEL_COLS { + MODEL_COL_NAME = 0, /* string */ + MODEL_COL_MCC = 1 /* the 'effective mcc' for this country */ +}; + + +static void +easysetup_country_combo_box_finalize (GObject *object) +{ + G_OBJECT_CLASS (easysetup_country_combo_box_parent_class)->finalize (object); +} + +static void +easysetup_country_combo_box_class_init (EasysetupCountryComboBoxClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (ModestEasysetupCountryComboBoxPrivate)); + + object_class->get_property = easysetup_country_combo_box_get_property; + object_class->set_property = easysetup_country_combo_box_set_property; + object_class->dispose = easysetup_country_combo_box_dispose; + object_class->finalize = easysetup_country_combo_box_finalize; +} + + + + +/* cluster mcc's, based on the list + * http://en.wikipedia.org/wiki/Mobile_country_code + */ +static int +effective_mcc (gint mcc) +{ + switch (mcc) { + case 405: return 404; /* india */ + case 441: return 440; /* japan */ + case 235: return 234; /* united kingdom */ + case 311: + case 312: + case 313: + case 314: + case 315: + case 316: return 310; /* united states */ + default: return mcc; + } +} + + +/* each line is of the form: + xxx logical_id + + NOTE: this function is NOT re-entrant, the out-param country + are static strings that should NOT be freed. and will change when + calling this function again + + also note, this function will return the "effective mcc", which + is the normalized mcc for a country - ie. even if the there + are multiple entries for the United States with various mccs, + this function will always return 310, even if the real mcc parsed + would be 314. see the 'effective_mcc' function above. +*/ +static int +parse_mcc_mapping_line (const char* line, char** country) +{ + int i, j; + char mcc[4]; /* the mcc code, always 3 bytes*/ + static char my_country[128]; + + if (!line) { + *country = NULL; + return 0; + } + + for (i = 3, j = 0; i < 128; ++i) { + char kar = line[i]; + if (kar == '\0') + break; + else if (kar < '_') + continue; + else + my_country [j++] = kar; + } + my_country[j] = '\0'; + + mcc[0] = line[0]; + mcc[1] = line[1]; + mcc[2] = line[2]; + mcc[3] = '\0'; + + *country = my_country; + + return effective_mcc ((int) strtol ((const char*)mcc, NULL, 10)); +} + +/** Note that the mcc_mapping file is installed + * by the operator-wizard-settings package. + */ +static void +load_from_file (EasysetupCountryComboBox *self, GtkListStore *liststore) +{ + ModestEasysetupCountryComboBoxPrivate *priv = MODEST_EASYSETUP_COUNTRY_COMBO_BOX_GET_PRIVATE (self); + + char line[MAX_LINE_LEN]; + guint previous_mcc = 0; + gchar *territory, *fallback = NULL; + gchar *current_locale; + + /* Get the territory specified for the current locale */ + territory = nl_langinfo (_NL_ADDRESS_COUNTRY_NAME); + + /* Tricky stuff, the translations of the OSSO countries does + not always correspond to the country names in locale + databases. Add all these cases here. sergio */ + if (!strcmp (territory, "United Kingdom")) + fallback = g_strdup ("UK"); + + current_locale = setlocale (LC_ALL ,NULL); + + FILE *file = modest_maemo_open_mcc_mapping_file (); + if (!file) { + g_warning("Could not open mcc_mapping file"); + return; + } + + while (fgets (line, MAX_LINE_LEN, file) != NULL) { + + int mcc; + char *country = NULL; + const gchar *name_translated, *english_translation; + + mcc = parse_mcc_mapping_line (line, &country); + if (!country || mcc == 0) { + g_warning ("%s: error parsing line: '%s'", __FUNCTION__, line); + continue; + } + + if (mcc == previous_mcc) { + /* g_warning ("already seen: %s", line); */ + continue; + } + previous_mcc = mcc; + + if (!priv->locale_mcc) { + english_translation = dgettext ("osso-countries", country); + if (!strcmp (english_translation, territory) || + (fallback && !strcmp (english_translation, fallback))) + priv->locale_mcc = mcc; + } + name_translated = dgettext ("osso-countries", country); + + /* Add the row to the model: */ + GtkTreeIter iter; + gtk_list_store_append (liststore, &iter); + gtk_list_store_set(liststore, &iter, MODEL_COL_MCC, mcc, MODEL_COL_NAME, name_translated, -1); + } + fclose (file); + + /* Sort the items: */ + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (liststore), + MODEL_COL_NAME, GTK_SORT_ASCENDING); + + g_free (fallback); +} + +static void +easysetup_country_combo_box_init (EasysetupCountryComboBox *self) +{ + ModestEasysetupCountryComboBoxPrivate *priv = MODEST_EASYSETUP_COUNTRY_COMBO_BOX_GET_PRIVATE (self); + priv->locale_mcc = 0; +} + +void +easysetup_country_combo_box_load_data(EasysetupCountryComboBox *self) +{ + GtkListStore *model; + + /* Create a tree model for the combo box, + * with a string for the name, and an int for the MCC ID. + * This must match our MODEL_COLS enum constants. + */ + model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT); + + /* Country column: + * The ID model column in not shown in the view. */ + GtkCellRenderer *renderer = gtk_cell_renderer_text_new (); + g_object_set (G_OBJECT (renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL); + +#if MODEST_HILDON_API < 2 + gtk_cell_layout_pack_start(GTK_CELL_LAYOUT (self), renderer, TRUE); + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (self), renderer, + "text", MODEL_COL_NAME, NULL); +#else + GtkWidget *selector = hildon_touch_selector_new (); + hildon_picker_button_set_selector (HILDON_PICKER_BUTTON (self), HILDON_TOUCH_SELECTOR (selector)); + hildon_touch_selector_append_column (hildon_picker_button_get_selector (HILDON_PICKER_BUTTON (self)), + GTK_TREE_MODEL (model), + renderer, "text", MODEL_COL_NAME, NULL); +#endif + + /* Fill the model with rows: */ + load_from_file (self, model); + + /* Set this _after_ loading from file, it makes loading faster */ +#if MODEST_HILDON_API < 2 + gtk_combo_box_set_model (GTK_COMBO_BOX (self), GTK_TREE_MODEL (model)); +#else + hildon_touch_selector_set_model (hildon_picker_button_get_selector (HILDON_PICKER_BUTTON (self)), + 0, GTK_TREE_MODEL (model)); +#endif +} + +EasysetupCountryComboBox* +easysetup_country_combo_box_new (void) +{ +#if MODEST_HILDON_API >= 2 + return g_object_new (MODEST_EASYSETUP_TYPE_COUNTRY_COMBO_BOX, + "arrangement", HILDON_BUTTON_ARRANGEMENT_VERTICAL, + "size", HILDON_SIZE_AUTO, + NULL); +#else + return g_object_new (MODEST_EASYSETUP_TYPE_COUNTRY_COMBO_BOX, + NULL); +#endif +} + +/** + * Returns the MCC number of the selected country, or 0 if no country was selected. + */ +gint +easysetup_country_combo_box_get_active_country_mcc (EasysetupCountryComboBox *self) +{ + GtkTreeIter active; + gboolean found; + +#if MODEST_HILDON_API < 2 + found = gtk_combo_box_get_active_iter (GTK_COMBO_BOX (self), &active); +#else + found = hildon_touch_selector_get_selected (hildon_picker_button_get_selector + (HILDON_PICKER_BUTTON (self)), 0, &active); +#endif + if (found) { + gint mcc = 0; +#if MODEST_HILDON_API < 2 + gtk_tree_model_get (gtk_combo_box_get_model (GTK_COMBO_BOX (self)), + &active, MODEL_COL_MCC, &mcc, -1); +#else + gtk_tree_model_get (hildon_touch_selector_get_model (hildon_picker_button_get_selector + (HILDON_PICKER_BUTTON (self)), + 0), + &active, MODEL_COL_MCC, &mcc, -1); +#endif + return mcc; + } + return 0; /* Failed. */ +} + + +/** + * Selects the MCC number of the selected country. + * Specify 0 to select no country. + */ +gboolean +easysetup_country_combo_box_set_active_country_locale (EasysetupCountryComboBox *self) +{ + ModestEasysetupCountryComboBoxPrivate *priv = MODEST_EASYSETUP_COUNTRY_COMBO_BOX_GET_PRIVATE (self); + GtkTreeIter iter; + gint current_mcc; + GtkTreeModel *model; + +#if MODEST_HILDON_API < 2 + model = gtk_combo_box_get_model (GTK_COMBO_BOX (self)); + g_message ("HILDON < 2"); +#else + model = hildon_touch_selector_get_model (hildon_picker_button_get_selector + (HILDON_PICKER_BUTTON (self)), 0); + g_message ("HILDON >= 2"); +#endif + if (!gtk_tree_model_get_iter_first (model, &iter)) + return FALSE; + do { + gtk_tree_model_get (model, &iter, MODEL_COL_MCC, ¤t_mcc, -1); + if (priv->locale_mcc == current_mcc) { +#if MODEST_HILDON_API < 2 + gtk_combo_box_set_active_iter (GTK_COMBO_BOX (self), &iter); +#else + hildon_touch_selector_select_iter (hildon_picker_button_get_selector + (HILDON_PICKER_BUTTON (self)), 0, + &iter, TRUE); +#endif + return TRUE; + } + } while (gtk_tree_model_iter_next (model, &iter)); + + return FALSE; /* not found */ +} + diff --git a/src/hildon2/modest-easysetup-country-combo-box.h b/src/hildon2/modest-easysetup-country-combo-box.h new file mode 100644 index 0000000..56f1741 --- /dev/null +++ b/src/hildon2/modest-easysetup-country-combo-box.h @@ -0,0 +1,93 @@ +/* Copyright (c) 2006, Nokia Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Nokia Corporation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _MODEST_EASYSETUP_COUNTRY_COMBO_BOX +#define _MODEST_EASYSETUP_COUNTRY_COMBO_BOX + +#if MODEST_HILDON_API < 2 +#include +#else +#include +#endif + +G_BEGIN_DECLS + +#define MODEST_EASYSETUP_TYPE_COUNTRY_COMBO_BOX easysetup_country_combo_box_get_type() + +#define MODEST_EASYSETUP_COUNTRY_COMBO_BOX(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + MODEST_EASYSETUP_TYPE_COUNTRY_COMBO_BOX, EasysetupCountryComboBox)) + +#define MODEST_EASYSETUP_COUNTRY_COMBO_BOX_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), \ + MODEST_EASYSETUP_TYPE_COUNTRY_COMBO_BOX, EasysetupCountryComboBoxClass)) + +#define MODEST_EASYSETUP_IS_COUNTRY_COMBO_BOX(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + MODEST_EASYSETUP_TYPE_COUNTRY_COMBO_BOX)) + +#define MODEST_EASYSETUP_IS_COUNTRY_COMBO_BOX_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + MODEST_EASYSETUP_TYPE_COUNTRY_COMBO_BOX)) + +#define MODEST_EASYSETUP_COUNTRY_COMBO_BOX_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + MODEST_EASYSETUP_TYPE_COUNTRY_COMBO_BOX, EasysetupCountryComboBoxClass)) + +#if MODEST_HILDON_API < 2 +typedef struct { + GtkComboBox parent; +} EasysetupCountryComboBox; + +typedef struct { + GtkComboBoxClass parent_class; +} EasysetupCountryComboBoxClass; +#else +typedef struct { + HildonPickerButton parent; +} EasysetupCountryComboBox; + +typedef struct { + HildonPickerButtonClass parent_class; +} EasysetupCountryComboBoxClass; +#endif +GType easysetup_country_combo_box_get_type (void); + +EasysetupCountryComboBox* easysetup_country_combo_box_new (void); + +void easysetup_country_combo_box_load_data(EasysetupCountryComboBox *self); + +gint easysetup_country_combo_box_get_active_country_mcc (EasysetupCountryComboBox *self); + + +gboolean easysetup_country_combo_box_set_active_country_locale (EasysetupCountryComboBox *self); + +G_END_DECLS + +#endif /* _MODEST_EASYSETUP_COUNTRY_COMBO_BOX */ diff --git a/src/hildon2/modest-easysetup-provider-combo-box.c b/src/hildon2/modest-easysetup-provider-combo-box.c new file mode 100644 index 0000000..1147a1d --- /dev/null +++ b/src/hildon2/modest-easysetup-provider-combo-box.c @@ -0,0 +1,332 @@ +/* Copyright (c) 2006, Nokia Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Nokia Corporation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "modest-easysetup-provider-combo-box.h" +#include +#include +#include +#include +#include +#include "modest-protocol-registry.h" +#include "modest-runtime.h" + +#include +#include /* For memcpy() */ + +/* Include config.h so that _() works: */ +#ifdef HAVE_CONFIG_H +#include +#endif + +G_DEFINE_TYPE (EasysetupProviderComboBox, easysetup_provider_combo_box, GTK_TYPE_COMBO_BOX); + +#define PROVIDER_COMBO_BOX_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), EASYSETUP_TYPE_PROVIDER_COMBO_BOX, EasysetupProviderComboBoxPrivate)) + +typedef struct _EasysetupProviderComboBoxPrivate EasysetupProviderComboBoxPrivate; + +struct _EasysetupProviderComboBoxPrivate +{ + GtkTreeModel *model; +}; + +static void +easysetup_provider_combo_box_get_property (GObject *object, guint property_id, + GValue *value, GParamSpec *pspec) +{ + switch (property_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +easysetup_provider_combo_box_set_property (GObject *object, guint property_id, + const GValue *value, GParamSpec *pspec) +{ + switch (property_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +easysetup_provider_combo_box_dispose (GObject *object) +{ + if (G_OBJECT_CLASS (easysetup_provider_combo_box_parent_class)->dispose) + G_OBJECT_CLASS (easysetup_provider_combo_box_parent_class)->dispose (object); +} + +static void +easysetup_provider_combo_box_finalize (GObject *object) +{ + EasysetupProviderComboBoxPrivate *priv = PROVIDER_COMBO_BOX_GET_PRIVATE (object); + + g_object_unref (G_OBJECT (priv->model)); + + G_OBJECT_CLASS (easysetup_provider_combo_box_parent_class)->finalize (object); +} + +static void +easysetup_provider_combo_box_class_init (EasysetupProviderComboBoxClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (EasysetupProviderComboBoxPrivate)); + + object_class->get_property = easysetup_provider_combo_box_get_property; + object_class->set_property = easysetup_provider_combo_box_set_property; + object_class->dispose = easysetup_provider_combo_box_dispose; + object_class->finalize = easysetup_provider_combo_box_finalize; +} + +enum MODEL_COLS { + MODEL_COL_ID, /* a string, not an int. */ + MODEL_COL_NAME, + MODEL_COL_ID_TYPE +}; + + +/* + * strictly, we should sort providers with mcc=0 after the other ones.... but, we don't have + * that info here, so ignoring for now. + */ +static gint +provider_sort_func (GtkTreeModel *model, GtkTreeIter *iter1, GtkTreeIter *iter2, gpointer user_data) +{ + gchar *prov1, *prov2; + gint retval; + + gtk_tree_model_get (model, iter1, MODEL_COL_NAME, &prov1, -1); + gtk_tree_model_get (model, iter2, MODEL_COL_NAME, &prov2, -1); + + if (strcmp (prov1, prov2) == 0) + retval = 0; + else if (strcmp (_("mcen_va_serviceprovider_other"), prov1) == 0) + retval = -1; + else if (strcmp (_("mcen_va_serviceprovider_other"), prov2) == 0) + retval = 1; + else + retval = modest_text_utils_utf8_strcmp (prov1, prov2, TRUE); + + g_free (prov1); + g_free (prov2); + + return retval; +} + +static void +easysetup_provider_combo_box_init (EasysetupProviderComboBox *self) +{ + EasysetupProviderComboBoxPrivate *priv = PROVIDER_COMBO_BOX_GET_PRIVATE (self); + + /* Create a tree model for the combo box, + * with a string for the name, and a string for the ID (e.g. "vodafone.it"), and the mcc + * This must match our MODEL_COLS enum constants. + */ + priv->model = GTK_TREE_MODEL (gtk_list_store_new (3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT)); + + /* Setup the combo box: */ + GtkComboBox *combobox = GTK_COMBO_BOX (self); + gtk_combo_box_set_model (combobox, priv->model); + + /* Provider column: + * The ID model column in not shown in the view. */ + GtkCellRenderer *renderer = gtk_cell_renderer_text_new (); + gtk_cell_layout_pack_start(GTK_CELL_LAYOUT (combobox), renderer, TRUE); + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combobox), renderer, "text", MODEL_COL_NAME, NULL); + + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(priv->model), + MODEL_COL_NAME, GTK_SORT_ASCENDING); + gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(priv->model), + MODEL_COL_NAME, + (GtkTreeIterCompareFunc)provider_sort_func, + NULL, NULL); +} + +EasysetupProviderComboBox* +easysetup_provider_combo_box_new (void) +{ + return g_object_new (EASYSETUP_TYPE_PROVIDER_COMBO_BOX, NULL); +} + +void +easysetup_provider_combo_box_fill (EasysetupProviderComboBox *combobox, + ModestPresets *presets, + gint mcc) +{ + GtkTreeIter other_iter; + EasysetupProviderComboBoxPrivate *priv; + GtkListStore *liststore; + GSList *provider_ids_used_already = NULL, *provider_protos, *tmp; + gchar ** provider_ids = NULL; + gchar ** provider_names; + gchar ** iter_provider_names; + gchar ** iter_provider_ids; + ModestProtocolRegistry *registry; + + g_return_if_fail (EASYSETUP_IS_PROVIDER_COMBO_BOX(combobox)); + + priv = PROVIDER_COMBO_BOX_GET_PRIVATE (combobox); + liststore = GTK_LIST_STORE (priv->model); + gtk_list_store_clear (liststore); + provider_names = modest_presets_get_providers (presets, mcc, TRUE, &provider_ids); + + iter_provider_names = provider_names; + iter_provider_ids = provider_ids; + + while(iter_provider_names && *iter_provider_names && iter_provider_ids && *iter_provider_ids) { + const gchar* provider_name = *iter_provider_names; + const gchar* provider_id = *iter_provider_ids; + + /* Prevent duplicate providers: */ + if (g_slist_find_custom (provider_ids_used_already, + provider_id, (GCompareFunc)strcmp) == NULL) { + /* printf("debug: provider_name=%s\n", provider_name); */ + + /* Add the row: */ + GtkTreeIter iter; + gtk_list_store_append (liststore, &iter); + + gtk_list_store_set(liststore, &iter, + MODEL_COL_ID, provider_id, + MODEL_COL_NAME, provider_name, + MODEL_COL_ID_TYPE, EASYSETUP_PROVIDER_COMBO_BOX_ID_PROVIDER, + -1); + + provider_ids_used_already = g_slist_prepend ( + provider_ids_used_already, (gpointer)g_strdup (provider_id)); + } + + ++iter_provider_names; + ++iter_provider_ids; + } + + /* Free the result of modest_presets_get_providers() + * as specified by its documentation: */ + g_strfreev (provider_names); + g_strfreev (provider_ids); + + /* Add the provider protocols */ + registry = modest_runtime_get_protocol_registry (); + provider_protos = modest_protocol_registry_get_by_tag (registry, + MODEST_PROTOCOL_REGISTRY_PROVIDER_PROTOCOLS); + tmp = provider_protos; + while (tmp) { + GtkTreeIter iter; + ModestProtocol *proto = MODEST_PROTOCOL (tmp->data); + const gchar *name = modest_protocol_get_display_name (proto); + + /* only add store protocols, no need to duplicate them */ + if (modest_protocol_registry_protocol_type_has_tag (registry, + modest_protocol_get_type_id (proto), + MODEST_PROTOCOL_REGISTRY_STORE_PROTOCOLS)) { + gtk_list_store_append (liststore, &iter); + gtk_list_store_set (liststore, &iter, + MODEL_COL_ID, modest_protocol_get_name (proto), + MODEL_COL_NAME, name, + MODEL_COL_ID_TYPE, EASYSETUP_PROVIDER_COMBO_BOX_ID_PLUGIN_PROTOCOL, + -1); + } + tmp = g_slist_next (tmp); + } + g_slist_free (provider_protos); + + /* Add the "Other" item: */ + /* Note that ID 0 means "Other" for us: */ + gtk_list_store_prepend (liststore, &other_iter); + gtk_list_store_set (liststore, &other_iter, + MODEL_COL_ID, 0, + MODEL_COL_NAME, _("mcen_va_serviceprovider_other"), + MODEL_COL_ID_TYPE, EASYSETUP_PROVIDER_COMBO_BOX_ID_OTHER, + -1); + + /* Select the "Other" item: */ + gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combobox), &other_iter); + + g_slist_foreach (provider_ids_used_already, (GFunc)g_free, NULL); + g_slist_free (provider_ids_used_already); +} + +/** + * Returns the MCC number of the selected provider, + * or NULL if no provider was selected, or "Other" was selected. + */ +gchar* +easysetup_provider_combo_box_get_active_provider_id (EasysetupProviderComboBox *combobox) +{ + GtkTreeIter active; + + g_return_val_if_fail (EASYSETUP_IS_PROVIDER_COMBO_BOX(combobox), NULL); + + const gboolean found = gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combobox), &active); + if (found) { + EasysetupProviderComboBoxPrivate *priv = PROVIDER_COMBO_BOX_GET_PRIVATE (combobox); + + gchar *id = NULL; + gtk_tree_model_get (priv->model, &active, MODEL_COL_ID, &id, -1); + return g_strdup(id); + } + + return NULL; /* Failed. */ +} + +void +easysetup_provider_combo_box_set_others_provider (EasysetupProviderComboBox *combobox) +{ + GtkTreeModel *model; + GtkTreeIter others_iter; + + g_return_if_fail (EASYSETUP_IS_PROVIDER_COMBO_BOX(combobox)); + + model = gtk_combo_box_get_model (GTK_COMBO_BOX (combobox)); + + if (gtk_tree_model_get_iter_first (model, &others_iter)) + gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combobox), &others_iter); +} + +EasysetupProviderComboBoxIdType +easysetup_provider_combo_box_get_active_id_type (EasysetupProviderComboBox *combobox) +{ + GtkTreeIter active; + + g_return_val_if_fail (EASYSETUP_IS_PROVIDER_COMBO_BOX (combobox), + EASYSETUP_PROVIDER_COMBO_BOX_ID_OTHER); + + if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combobox), &active)) { + EasysetupProviderComboBoxPrivate *priv = PROVIDER_COMBO_BOX_GET_PRIVATE (combobox); + EasysetupProviderComboBoxIdType id_type; + + gtk_tree_model_get (priv->model, &active, MODEL_COL_ID_TYPE, &id_type, -1); + return id_type; + } else { + /* Fallback to other */ + return EASYSETUP_PROVIDER_COMBO_BOX_ID_OTHER; + } +} diff --git a/src/hildon2/modest-easysetup-provider-combo-box.h b/src/hildon2/modest-easysetup-provider-combo-box.h new file mode 100644 index 0000000..d4bc283 --- /dev/null +++ b/src/hildon2/modest-easysetup-provider-combo-box.h @@ -0,0 +1,96 @@ +/* Copyright (c) 2006, Nokia Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Nokia Corporation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _EASYSETUP_PROVIDER_COMBO_BOX +#define _EASYSETUP_PROVIDER_COMBO_BOX + +#include +#include "modest-presets.h" + +G_BEGIN_DECLS + +#define EASYSETUP_TYPE_PROVIDER_COMBO_BOX easysetup_provider_combo_box_get_type() + +#define EASYSETUP_PROVIDER_COMBO_BOX(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + EASYSETUP_TYPE_PROVIDER_COMBO_BOX, EasysetupProviderComboBox)) + +#define EASYSETUP_PROVIDER_COMBO_BOX_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), \ + EASYSETUP_TYPE_PROVIDER_COMBO_BOX, EasysetupProviderComboBoxClass)) + +#define EASYSETUP_IS_PROVIDER_COMBO_BOX(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + EASYSETUP_TYPE_PROVIDER_COMBO_BOX)) + +#define EASYSETUP_IS_PROVIDER_COMBO_BOX_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + EASYSETUP_TYPE_PROVIDER_COMBO_BOX)) + +#define EASYSETUP_PROVIDER_COMBO_BOX_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + EASYSETUP_TYPE_PROVIDER_COMBO_BOX, EasysetupProviderComboBoxClass)) + +/** The thype of the ID + * + * this means the value of returned by get_active_provider_id will be + * different depending on the value returned by get_active_id_type + * + * If the selected option is a provider then the ID will be the provider ID + * If the selected option is "Other..." the the ID will be 0 + * If the selected option is a provider protocol () the the ID will be protocol name + **/ +typedef enum { + EASYSETUP_PROVIDER_COMBO_BOX_ID_PROVIDER, + EASYSETUP_PROVIDER_COMBO_BOX_ID_OTHER, + EASYSETUP_PROVIDER_COMBO_BOX_ID_PLUGIN_PROTOCOL +} EasysetupProviderComboBoxIdType; + +typedef struct { + GtkComboBox parent; +} EasysetupProviderComboBox; + +typedef struct { + GtkComboBoxClass parent_class; +} EasysetupProviderComboBoxClass; + +GType easysetup_provider_combo_box_get_type (void); + +EasysetupProviderComboBox* easysetup_provider_combo_box_new (void); + +void easysetup_provider_combo_box_fill (EasysetupProviderComboBox *combobox, ModestPresets *presets, + gint mcc); + +gchar* easysetup_provider_combo_box_get_active_provider_id (EasysetupProviderComboBox *combobox); + +EasysetupProviderComboBoxIdType easysetup_provider_combo_box_get_active_id_type (EasysetupProviderComboBox *combobox); + +G_END_DECLS + +#endif /* _EASYSETUP_PROVIDER_COMBO_BOX */ diff --git a/src/hildon2/modest-easysetup-servertype-combo-box.c b/src/hildon2/modest-easysetup-servertype-combo-box.c new file mode 100644 index 0000000..ab54656 --- /dev/null +++ b/src/hildon2/modest-easysetup-servertype-combo-box.c @@ -0,0 +1,276 @@ +/* Copyright (c) 2006, Nokia Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Nokia Corporation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include "modest-easysetup-servertype-combo-box.h" +#include +#include +#include +#include + +#include +#include /* For memcpy() */ + +/* Include config.h so that _() works: */ +#ifdef HAVE_CONFIG_H +#include +#endif + +G_DEFINE_TYPE (EasysetupServertypeComboBox, easysetup_servertype_combo_box, GTK_TYPE_COMBO_BOX); + +#define SERVERTYPE_COMBO_BOX_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), EASYSETUP_TYPE_SERVERTYPE_COMBO_BOX, EasysetupServertypeComboBoxPrivate)) + +typedef struct _EasysetupServertypeComboBoxPrivate EasysetupServertypeComboBoxPrivate; + +struct _EasysetupServertypeComboBoxPrivate +{ + GtkTreeModel *model; +}; + +static void +easysetup_servertype_combo_box_get_property (GObject *object, guint property_id, + GValue *value, GParamSpec *pspec) +{ + switch (property_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +easysetup_servertype_combo_box_set_property (GObject *object, guint property_id, + const GValue *value, GParamSpec *pspec) +{ + switch (property_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +easysetup_servertype_combo_box_dispose (GObject *object) +{ + if (G_OBJECT_CLASS (easysetup_servertype_combo_box_parent_class)->dispose) + G_OBJECT_CLASS (easysetup_servertype_combo_box_parent_class)->dispose (object); +} + +static void +easysetup_servertype_combo_box_finalize (GObject *object) +{ + EasysetupServertypeComboBoxPrivate *priv = SERVERTYPE_COMBO_BOX_GET_PRIVATE (object); + + g_object_unref (G_OBJECT (priv->model)); + + G_OBJECT_CLASS (easysetup_servertype_combo_box_parent_class)->finalize (object); +} + +static void +easysetup_servertype_combo_box_class_init (EasysetupServertypeComboBoxClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (EasysetupServertypeComboBoxPrivate)); + + object_class->get_property = easysetup_servertype_combo_box_get_property; + object_class->set_property = easysetup_servertype_combo_box_set_property; + object_class->dispose = easysetup_servertype_combo_box_dispose; + object_class->finalize = easysetup_servertype_combo_box_finalize; +} + +enum MODEL_COLS { + MODEL_COL_NAME = 0, /* a string */ + MODEL_COL_ID = 1 /* an int. */ +}; + +static void +easysetup_servertype_combo_box_init (EasysetupServertypeComboBox *self) +{ + EasysetupServertypeComboBoxPrivate *priv = SERVERTYPE_COMBO_BOX_GET_PRIVATE (self); + + /* Create a tree model for the combo box, + * with a string for the name, and an ID for the servertype. + * This must match our MODEL_COLS enum constants. + */ + priv->model = GTK_TREE_MODEL (gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT)); + + /* Setup the combo box: */ + GtkComboBox *combobox = GTK_COMBO_BOX (self); + gtk_combo_box_set_model (combobox, priv->model); + + /* Servertype column: + * The ID model column in not shown in the view. */ + GtkCellRenderer *renderer = gtk_cell_renderer_text_new (); + gtk_cell_layout_pack_start(GTK_CELL_LAYOUT (combobox), renderer, TRUE); + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combobox), renderer, + "text", MODEL_COL_NAME, NULL); +} + +static void +easysetup_servertype_combo_box_fill (EasysetupServertypeComboBox *combobox, + gboolean filter_providers) +{ + EasysetupServertypeComboBoxPrivate *priv; + GtkListStore *liststore; + ModestProtocolRegistry *protocol_registry; + GSList *remote_protocols, *node; + GtkTreeIter iter; + + /* Remove any existing rows: */ + priv = SERVERTYPE_COMBO_BOX_GET_PRIVATE (combobox); + protocol_registry = modest_runtime_get_protocol_registry (); + remote_protocols = modest_protocol_registry_get_by_tag (protocol_registry, MODEST_PROTOCOL_REGISTRY_REMOTE_STORE_PROTOCOLS); + + liststore = GTK_LIST_STORE (priv->model); + gtk_list_store_clear (liststore); + + for (node = remote_protocols; node != NULL; node = g_slist_next (node)) { + ModestProtocol* protocol; + gboolean add = TRUE; + + protocol = (ModestProtocol *) node->data; + + /* Do not include the protocols that would be listed + in the providers combo */ + if (filter_providers) + if (modest_protocol_registry_protocol_type_is_provider (protocol_registry, + modest_protocol_get_type_id (protocol))) { + add = FALSE; + } + + if (add) { + gtk_list_store_append (liststore, &iter); + gtk_list_store_set (liststore, &iter, + MODEL_COL_ID, + modest_protocol_get_type_id (protocol), + MODEL_COL_NAME, + modest_protocol_get_display_name (protocol), + -1); + } + } + + g_slist_free (remote_protocols); +} + +EasysetupServertypeComboBox* +easysetup_servertype_combo_box_new (gboolean filter_providers) +{ + EasysetupServertypeComboBox *combo; + + combo = g_object_new (EASYSETUP_TYPE_SERVERTYPE_COMBO_BOX, NULL); + + /* Fill the combo */ + easysetup_servertype_combo_box_fill (combo, filter_providers); + + return combo; +} + +/** + * Returns the selected servertype, + * or MODEST_PROTOCOL_REGISTRY_TYPE_INVALID if no servertype was selected. + */ +ModestProtocolType +easysetup_servertype_combo_box_get_active_servertype (EasysetupServertypeComboBox *combobox) +{ + GtkTreeIter active; + gboolean found; + + found = gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combobox), &active); + if (found) { + EasysetupServertypeComboBoxPrivate *priv; + ModestProtocolType servertype; + + priv = SERVERTYPE_COMBO_BOX_GET_PRIVATE (combobox); + + servertype = MODEST_PROTOCOL_REGISTRY_TYPE_INVALID; + gtk_tree_model_get (priv->model, &active, MODEL_COL_ID, &servertype, -1); + return servertype; + } + + return MODEST_PROTOCOL_REGISTRY_TYPE_INVALID; /* Failed. */ +} + +/* This allows us to pass more than one piece of data to the signal handler, + * and get a result: */ +typedef struct +{ + EasysetupServertypeComboBox* self; + ModestProtocolType id; + gboolean found; +} ForEachData; + +static gboolean +on_model_foreach_select_id(GtkTreeModel *model, + GtkTreePath *path, GtkTreeIter *iter, gpointer user_data) +{ + ModestProtocolType id = MODEST_PROTOCOL_REGISTRY_TYPE_INVALID; + ForEachData *state = (ForEachData*)(user_data); + + /* Select the item if it has the matching ID: */ + gtk_tree_model_get (model, iter, MODEL_COL_ID, &id, -1); + if(id == state->id) { + gtk_combo_box_set_active_iter (GTK_COMBO_BOX (state->self), iter); + + state->found = TRUE; + return TRUE; /* Stop walking the tree. */ + } + + return FALSE; /* Keep walking the tree. */ +} + +/** + * Selects the specified servertype, + * or MODEST_PROTOCOL_TRANSPORT_STORE_UNKNOWN if no servertype was selected. + */ +gboolean +easysetup_servertype_combo_box_set_active_servertype (EasysetupServertypeComboBox *combobox, ModestProtocolType servertype) +{ + EasysetupServertypeComboBoxPrivate *priv; + ForEachData *state; + gboolean result; + + priv = SERVERTYPE_COMBO_BOX_GET_PRIVATE (combobox); + + /* Create a state instance so we can send two items of data to the signal handler: */ + state = g_new0 (ForEachData, 1); + state->self = combobox; + state->id = servertype; + state->found = FALSE; + + /* Look at each item, and select the one with the correct ID: */ + gtk_tree_model_foreach (priv->model, &on_model_foreach_select_id, state); + + result = state->found; + + /* Free the state instance: */ + g_free(state); + + return result; +} + diff --git a/src/hildon2/modest-easysetup-servertype-combo-box.h b/src/hildon2/modest-easysetup-servertype-combo-box.h new file mode 100644 index 0000000..bb7a6b4 --- /dev/null +++ b/src/hildon2/modest-easysetup-servertype-combo-box.h @@ -0,0 +1,79 @@ +/* Copyright (c) 2006, Nokia Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Nokia Corporation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _EASYSETUP_SERVERTYPE_COMBO_BOX +#define _EASYSETUP_SERVERTYPE_COMBO_BOX + +#include +#include "modest-protocol-registry.h" + +G_BEGIN_DECLS + +#define EASYSETUP_TYPE_SERVERTYPE_COMBO_BOX easysetup_servertype_combo_box_get_type() + +#define EASYSETUP_SERVERTYPE_COMBO_BOX(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + EASYSETUP_TYPE_SERVERTYPE_COMBO_BOX, EasysetupServertypeComboBox)) + +#define EASYSETUP_SERVERTYPE_COMBO_BOX_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), \ + EASYSETUP_TYPE_SERVERTYPE_COMBO_BOX, EasysetupServertypeComboBoxClass)) + +#define EASYSETUP_IS_SERVERTYPE_COMBO_BOX(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + EASYSETUP_TYPE_SERVERTYPE_COMBO_BOX)) + +#define EASYSETUP_IS_SERVERTYPE_COMBO_BOX_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + EASYSETUP_TYPE_SERVERTYPE_COMBO_BOX)) + +#define EASYSETUP_SERVERTYPE_COMBO_BOX_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + EASYSETUP_TYPE_SERVERTYPE_COMBO_BOX, EasysetupServertypeComboBoxClass)) + +typedef struct { + GtkComboBox parent; +} EasysetupServertypeComboBox; + +typedef struct { + GtkComboBoxClass parent_class; +} EasysetupServertypeComboBoxClass; + +GType easysetup_servertype_combo_box_get_type (void); + +EasysetupServertypeComboBox* easysetup_servertype_combo_box_new (gboolean filter_providers); + +ModestProtocolType easysetup_servertype_combo_box_get_active_servertype (EasysetupServertypeComboBox *combobox); + +gboolean easysetup_servertype_combo_box_set_active_servertype (EasysetupServertypeComboBox *combobox, ModestProtocolType servertype); + + +G_END_DECLS + +#endif /* _EASYSETUP_PROVIDER_COMBO_BOX */ diff --git a/src/hildon2/modest-easysetup-wizard-dialog.c b/src/hildon2/modest-easysetup-wizard-dialog.c new file mode 100644 index 0000000..9e61d43 --- /dev/null +++ b/src/hildon2/modest-easysetup-wizard-dialog.c @@ -0,0 +1,2101 @@ + +/* Copyright (c) 2006, Nokia Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Nokia Corporation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include "modest-easysetup-wizard-dialog.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "maemo/easysetup/modest-easysetup-country-combo-box.h" +#include "maemo/easysetup/modest-easysetup-provider-combo-box.h" +#include "maemo/easysetup/modest-easysetup-servertype-combo-box.h" +#include "widgets/modest-validating-entry.h" +#include "modest-text-utils.h" +#include "modest-conf.h" +#include "modest-defs.h" +#include "modest-account-mgr.h" +#include "modest-signal-mgr.h" +#include "modest-account-mgr-helpers.h" +#include "modest-runtime.h" /* For modest_runtime_get_account_mgr(). */ +#include "maemo/modest-connection-specific-smtp-window.h" +#include "widgets/modest-ui-constants.h" +#include "widgets/modest-default-account-settings-dialog.h" +#include "widgets/modest-easysetup-wizard-page.h" +#include "maemo/modest-maemo-utils.h" +#include "modest-utils.h" +#include "maemo/modest-hildon-includes.h" +#include "maemo/modest-maemo-security-options-view.h" +#include + +/* Include config.h so that _() works: */ +#ifdef HAVE_CONFIG_H +#include +#endif + +G_DEFINE_TYPE (ModestEasysetupWizardDialog, modest_easysetup_wizard_dialog, MODEST_TYPE_WIZARD_DIALOG); + +#define MODEST_EASYSETUP_WIZARD_DIALOG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \ + MODEST_TYPE_EASYSETUP_WIZARD_DIALOG, \ + ModestEasysetupWizardDialogPrivate)) + +typedef struct _ModestEasysetupWizardDialogPrivate ModestEasysetupWizardDialogPrivate; + + +typedef enum { + MODEST_EASYSETUP_WIZARD_DIALOG_INCOMING_CHANGED = 0x01, + MODEST_EASYSETUP_WIZARD_DIALOG_OUTGOING_CHANGED = 0x02 +} ModestEasysetupWizardDialogServerChanges; + +struct _ModestEasysetupWizardDialogPrivate +{ + ModestPresets *presets; + + /* Remember what fields the user edited manually to not prefill them + * again. */ + ModestEasysetupWizardDialogServerChanges server_changes; + + /* Check if the user changes a field to show a confirmation dialog */ + gboolean dirty; + + /* If we have a pending load of settings or not. */ + gboolean pending_load_settings; + + /* Used by derived widgets to query existing accounts, + * and to create new accounts: */ + ModestAccountMgr *account_manager; + ModestAccountSettings *settings; + + /* notebook pages: */ + GtkWidget *page_welcome; + + GtkWidget *page_account_details; + GtkWidget *combo_account_country; + GtkWidget *combo_account_serviceprovider; + GtkWidget *entry_account_title; + + GtkWidget *page_user_details; + GtkWidget *entry_user_name; + GtkWidget *entry_user_username; + GtkWidget *entry_user_password; + GtkWidget *entry_user_email; + + GtkWidget *page_complete_easysetup; + + GtkWidget *page_custom_incoming; + GtkWidget *combo_incoming_servertype; + GtkWidget *caption_incoming; + GtkWidget *entry_incomingserver; + + /* Security widgets */ + GtkWidget *incoming_security; + GtkWidget *outgoing_security; + + GtkWidget *page_custom_outgoing; + GtkWidget *entry_outgoingserver; + GtkWidget *combo_outgoing_security; + GtkWidget *combo_outgoing_auth; + GtkWidget *checkbox_outgoing_smtp_specific; + GtkWidget *button_outgoing_smtp_servers; + + GtkWidget *page_complete_customsetup; + + ModestProtocolType last_plugin_protocol_selected; + GSList *missing_data_signals; +}; + +static void save_to_settings (ModestEasysetupWizardDialog *self); +static void real_enable_buttons (ModestWizardDialog *dialog, gboolean enable_next); + +static gboolean +on_delete_event (GtkWidget *widget, + GdkEvent *event, + ModestEasysetupWizardDialog *wizard) +{ + gtk_dialog_response (GTK_DIALOG (wizard), GTK_RESPONSE_CANCEL); + return TRUE; +} + +static void +on_easysetup_changed(GtkWidget* widget, ModestEasysetupWizardDialog* wizard) +{ + ModestEasysetupWizardDialogPrivate* priv = MODEST_EASYSETUP_WIZARD_DIALOG_GET_PRIVATE(wizard); + g_return_if_fail (priv != NULL); + priv->dirty = TRUE; +} + +static void +modest_easysetup_wizard_dialog_dispose (GObject *object) +{ + if (G_OBJECT_CLASS (modest_easysetup_wizard_dialog_parent_class)->dispose) + G_OBJECT_CLASS (modest_easysetup_wizard_dialog_parent_class)->dispose (object); +} + +static void +modest_easysetup_wizard_dialog_finalize (GObject *object) +{ + ModestEasysetupWizardDialog *self = MODEST_EASYSETUP_WIZARD_DIALOG (object); + ModestEasysetupWizardDialogPrivate *priv = MODEST_EASYSETUP_WIZARD_DIALOG_GET_PRIVATE (self); + + if (priv->account_manager) + g_object_unref (G_OBJECT (priv->account_manager)); + + if (priv->presets) + modest_presets_destroy (priv->presets); + + if (priv->settings) + g_object_unref (priv->settings); + + if (priv->missing_data_signals) { + modest_signal_mgr_disconnect_all_and_destroy (priv->missing_data_signals); + priv->missing_data_signals = NULL; + } + + G_OBJECT_CLASS (modest_easysetup_wizard_dialog_parent_class)->finalize (object); +} + +static void +create_subsequent_easysetup_pages (ModestEasysetupWizardDialog *self); + +static void +set_default_custom_servernames(ModestEasysetupWizardDialog *dialog); + +static void on_combo_servertype_changed(GtkComboBox *combobox, gpointer user_data); + +static gint +get_port_from_protocol (ModestProtocolType server_type, + gboolean alternate_port) +{ + int server_port = 0; + ModestProtocol *protocol; + ModestProtocolRegistry *protocol_registry; + + protocol_registry = modest_runtime_get_protocol_registry (); + protocol = modest_protocol_registry_get_protocol_by_type (protocol_registry, server_type); + + if (alternate_port) { + server_port = modest_account_protocol_get_alternate_port (MODEST_ACCOUNT_PROTOCOL (protocol)); + } else { + server_port = modest_account_protocol_get_port (MODEST_ACCOUNT_PROTOCOL (protocol)); + } + return server_port; +} + +/* static GList* */ +/* check_for_supported_auth_methods (ModestEasysetupWizardDialog* self) */ +/* { */ +/* GError *error = NULL; */ +/* ModestProtocolType protocol_type; */ +/* const gchar* hostname; */ +/* const gchar* username; */ +/* ModestProtocolType security_protocol_incoming_type; */ +/* ModestProtocolRegistry *registry; */ +/* int port_num; */ +/* GList *list_auth_methods; */ +/* ModestEasysetupWizardDialogPrivate *priv; */ + +/* priv = MODEST_EASYSETUP_WIZARD_DIALOG_GET_PRIVATE (self); */ +/* registry = modest_runtime_get_protocol_registry (); */ +/* protocol_type = easysetup_servertype_combo_box_get_active_servertype ( */ +/* EASYSETUP_SERVERTYPE_COMBO_BOX (priv->combo_incoming_servertype)); */ +/* hostname = gtk_entry_get_text(GTK_ENTRY(priv->entry_incomingserver)); */ +/* username = gtk_entry_get_text(GTK_ENTRY(priv->entry_user_username)); */ +/* security_protocol_incoming_type = modest_serversecurity_combo_box_get_active_serversecurity */ +/* (MODEST_SERVERSECURITY_COMBO_BOX (priv->combo_incoming_security)); */ +/* port_num = get_port_from_protocol(protocol_type, FALSE); */ +/* list_auth_methods = modest_utils_get_supported_secure_authentication_methods (protocol_type, hostname, port_num, */ +/* username, GTK_WINDOW (self), &error); */ + +/* if (list_auth_methods) { */ +/* /\* TODO: Select the correct method *\/ */ +/* GList* list = NULL; */ +/* GList* method; */ +/* for (method = list_auth_methods; method != NULL; method = g_list_next(method)) { */ +/* ModestProtocolType auth_protocol_type = (ModestProtocolType) (GPOINTER_TO_INT(method->data)); */ +/* if (modest_protocol_registry_protocol_type_has_tag (registry, auth_protocol_type, */ +/* MODEST_PROTOCOL_REGISTRY_SECURE_PROTOCOLS)) { */ +/* list = g_list_append(list, GINT_TO_POINTER(auth_protocol_type)); */ +/* } */ +/* } */ + +/* g_list_free(list_auth_methods); */ + +/* if (list) */ +/* return list; */ +/* } */ + +/* if(error == NULL || error->domain != modest_utils_get_supported_secure_authentication_error_quark() || */ +/* error->code != MODEST_UTILS_GET_SUPPORTED_SECURE_AUTHENTICATION_ERROR_CANCELED) */ +/* { */ +/* modest_platform_information_banner (GTK_WIDGET(self), NULL, */ +/* _("mcen_ib_unableto_discover_auth_methods")); */ +/* } */ + +/* if(error != NULL) */ +/* g_error_free(error); */ + +/* return NULL; */ +/* } */ + +/* static gboolean check_has_supported_auth_methods(ModestEasysetupWizardDialog* self) */ +/* { */ +/* GList* methods = check_for_supported_auth_methods(self); */ +/* if (!methods) */ +/* { */ +/* return FALSE; */ +/* } */ + +/* g_list_free(methods); */ +/* return TRUE; */ +/* } */ + +/* static ModestProtocolType check_first_supported_auth_method(ModestEasysetupWizardDialog* self) */ +/* { */ +/* ModestProtocolType result; */ + +/* result = MODEST_PROTOCOLS_AUTH_PASSWORD; */ + +/* GList* methods = check_for_supported_auth_methods(self); */ +/* if (methods) */ +/* { */ +/* /\* Use the first one: *\/ */ +/* result = (ModestProtocolType) (GPOINTER_TO_INT(methods->data)); */ +/* g_list_free(methods); */ +/* } */ + +/* return result; */ +/* } */ + +static void +invoke_enable_buttons_vfunc (ModestEasysetupWizardDialog *wizard_dialog) +{ + ModestWizardDialogClass *klass = MODEST_WIZARD_DIALOG_GET_CLASS (wizard_dialog); + + /* Call the vfunc, which may be overridden by derived classes: */ + if (klass->enable_buttons) { + GtkNotebook *notebook = NULL; + g_object_get (wizard_dialog, "wizard-notebook", ¬ebook, NULL); + + const gint current_page_num = gtk_notebook_get_current_page (notebook); + if (current_page_num == -1) + return; + + GtkWidget* current_page_widget = gtk_notebook_get_nth_page (notebook, current_page_num); + (*(klass->enable_buttons))(MODEST_WIZARD_DIALOG (wizard_dialog), current_page_widget); + } +} + +static void +on_caption_entry_changed (GtkEditable *editable, gpointer user_data) +{ + ModestEasysetupWizardDialog *self = MODEST_EASYSETUP_WIZARD_DIALOG (user_data); + g_assert(self); + invoke_enable_buttons_vfunc(self); +} + +static void +on_caption_combobox_changed (GtkComboBox *widget, gpointer user_data) +{ + ModestEasysetupWizardDialog *self = MODEST_EASYSETUP_WIZARD_DIALOG (user_data); + g_assert(self); + invoke_enable_buttons_vfunc(self); +} + +/** This is a convenience function to create a caption containing a mandatory widget. + * When the widget is edited, the enable_buttons() vfunc will be called. + */ +static GtkWidget* +create_caption_new_with_asterisk (ModestEasysetupWizardDialog *self, + GtkSizeGroup *group, + const gchar *value, + GtkWidget *control, + GtkWidget *icon, + HildonCaptionStatus flag) +{ + GtkWidget *caption = NULL; + + /* Note: Previously, the translated strings already contained the "*", + * Comment out this code if they do again. + */ + /* Add a * character to indicate mandatory fields, + * as specified in our "Email UI Specification": */ + if (flag == HILDON_CAPTION_MANDATORY) { + gchar* title = g_strdup_printf("%s*", value); + caption = hildon_caption_new (group, title, control, icon, flag); + g_free(title); + } + else + caption = hildon_caption_new (group, value, control, icon, flag); + + /* Connect to the appropriate changed signal for the widget, + * so we can ask for the prev/next buttons to be enabled/disabled appropriately: + */ + if (GTK_IS_ENTRY (control)) { + g_signal_connect (G_OBJECT (control), "changed", + G_CALLBACK (on_caption_entry_changed), self); + + } + else if (GTK_IS_COMBO_BOX (control)) { + g_signal_connect (G_OBJECT (control), "changed", + G_CALLBACK (on_caption_combobox_changed), self); + } + + return caption; +} + +static GtkWidget* +create_page_welcome (ModestEasysetupWizardDialog *self) +{ + GtkWidget *box = gtk_vbox_new (FALSE, MODEST_MARGIN_NONE); + GtkWidget *label = gtk_label_new(_("mcen_ia_emailsetup_intro")); + gtk_label_set_line_wrap (GTK_LABEL (label), TRUE); + /* So that it is not truncated: */ + gtk_widget_set_size_request (label, 600, -1); + gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0); + gtk_widget_show (label); + gtk_widget_show (GTK_WIDGET (box)); + return GTK_WIDGET (box); +} + +#if MODEST_HILDON_API < 2 +static void +on_combo_account_country (GtkComboBox *widget, gpointer user_data) +#else +static void +on_combo_account_country (HildonTouchSelector *widget, gint column, gpointer user_data) +#endif +{ + ModestEasysetupWizardDialog *self = MODEST_EASYSETUP_WIZARD_DIALOG (user_data); + g_assert(self); + ModestEasysetupWizardDialogPrivate *priv = MODEST_EASYSETUP_WIZARD_DIALOG_GET_PRIVATE (self); + + priv->dirty = TRUE; + + /* Fill the providers combo, based on the selected country: */ + if (priv->presets != NULL) { + gint mcc = easysetup_country_combo_box_get_active_country_mcc ( + MODEST_EASYSETUP_COUNTRY_COMBO_BOX (priv->combo_account_country)); + easysetup_provider_combo_box_fill ( + EASYSETUP_PROVIDER_COMBO_BOX (priv->combo_account_serviceprovider), priv->presets, mcc); + } +} + +static void +on_combo_account_serviceprovider (GtkComboBox *widget, gpointer user_data) +{ + ModestEasysetupWizardDialog *self = MODEST_EASYSETUP_WIZARD_DIALOG (user_data); + g_assert(self); + ModestEasysetupWizardDialogPrivate *priv = MODEST_EASYSETUP_WIZARD_DIALOG_GET_PRIVATE (self); + + priv->dirty = TRUE; + + /* Fill the providers combo, based on the selected country: */ + gchar* provider_id = easysetup_provider_combo_box_get_active_provider_id ( + EASYSETUP_PROVIDER_COMBO_BOX (priv->combo_account_serviceprovider)); + + gchar* domain_name = NULL; + if(provider_id) + domain_name = modest_presets_get_domain (priv->presets, provider_id); + + if(!domain_name) + domain_name = g_strdup (MODEST_EXAMPLE_EMAIL_ADDRESS); + + if (priv->entry_user_email) + gtk_entry_set_text (GTK_ENTRY (priv->entry_user_email), domain_name); + + g_free (domain_name); + + g_free (provider_id); +} + +static void +on_entry_max (ModestValidatingEntry *self, gpointer user_data) +{ + modest_platform_information_banner (GTK_WIDGET (self), NULL, + _CS("ckdg_ib_maximum_characters_reached")); +} + +static GtkWidget* +create_page_account_details (ModestEasysetupWizardDialog *self) +{ + GtkWidget *box = gtk_vbox_new (FALSE, MODEST_MARGIN_NONE); + GtkWidget *label = gtk_label_new(_("mcen_ia_accountdetails")); + ModestEasysetupWizardDialogPrivate* priv = MODEST_EASYSETUP_WIZARD_DIALOG_GET_PRIVATE(self); + + gtk_label_set_line_wrap (GTK_LABEL (label), TRUE); + gtk_widget_set_size_request (label, 600, -1); + gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, MODEST_MARGIN_HALF); + gtk_widget_show (label); + + /* Create a size group to be used by all captions. + * Note that HildonCaption does not create a default size group if we do not specify one. + * We use GTK_SIZE_GROUP_HORIZONTAL, so that the widths are the same. */ + GtkSizeGroup* sizegroup = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); + + /* The country widgets: */ + priv->combo_account_country = GTK_WIDGET (easysetup_country_combo_box_new ()); + GtkWidget *caption = create_caption_new_with_asterisk (self, sizegroup, _("mcen_fi_country"), + priv->combo_account_country, NULL, HILDON_CAPTION_OPTIONAL); + gtk_widget_show (priv->combo_account_country); + gtk_box_pack_start (GTK_BOX (box), caption, FALSE, FALSE, MODEST_MARGIN_HALF); + gtk_widget_show (caption); + + /* connect to country combo's changed signal, so we can fill the provider combo: */ +#if MODEST_HILDON_API < 2 + g_signal_connect (G_OBJECT (priv->combo_account_country), "changed", + G_CALLBACK (on_combo_account_country), self); +#else + g_signal_connect (G_OBJECT (hildon_picker_button_get_selector + (HILDON_PICKER_BUTTON (priv->combo_account_country))), + "changed", + G_CALLBACK (on_combo_account_country), self); +#endif + + GtkWidget *separator = gtk_hseparator_new (); + gtk_box_pack_start (GTK_BOX (box), separator, FALSE, FALSE, MODEST_MARGIN_HALF); + gtk_widget_show (separator); + + /* The service provider widgets: */ + priv->combo_account_serviceprovider = GTK_WIDGET (easysetup_provider_combo_box_new ()); + gtk_widget_set_size_request (priv->combo_account_serviceprovider, 320, -1); + + caption = create_caption_new_with_asterisk (self, sizegroup, _("mcen_fi_serviceprovider"), + priv->combo_account_serviceprovider, NULL, HILDON_CAPTION_OPTIONAL); + gtk_widget_show (priv->combo_account_serviceprovider); + gtk_box_pack_start (GTK_BOX (box), caption, FALSE, FALSE, MODEST_MARGIN_HALF); + gtk_widget_show (caption); + + /* connect to providers combo's changed signal, so we can fill the email address: */ + g_signal_connect (G_OBJECT (priv->combo_account_serviceprovider), "changed", + G_CALLBACK (on_combo_account_serviceprovider), self); + + /* The description widgets: */ + priv->entry_account_title = GTK_WIDGET (modest_validating_entry_new ()); + g_signal_connect(G_OBJECT(priv->entry_account_title), "changed", + G_CALLBACK(on_easysetup_changed), self); + /* Do use auto-capitalization: */ + hildon_gtk_entry_set_input_mode (GTK_ENTRY (priv->entry_account_title), + HILDON_GTK_INPUT_MODE_FULL | HILDON_GTK_INPUT_MODE_AUTOCAP); + + /* Set a default account title, choosing one that does not already exist: */ + /* Note that this is irrelevant to the non-user visible name, which we will create later. */ + gchar* default_account_name_start = g_strdup (_("mcen_ia_emailsetup_defaultname")); + gchar* default_account_name = modest_account_mgr_get_unused_account_display_name ( + priv->account_manager, default_account_name_start); + g_free (default_account_name_start); + default_account_name_start = NULL; + + gtk_entry_set_text( GTK_ENTRY (priv->entry_account_title), default_account_name); + g_free (default_account_name); + default_account_name = NULL; + + caption = create_caption_new_with_asterisk (self, sizegroup, _("mcen_fi_account_title"), + priv->entry_account_title, NULL, HILDON_CAPTION_MANDATORY); + gtk_widget_show (priv->entry_account_title); + gtk_box_pack_start (GTK_BOX (box), caption, FALSE, FALSE, MODEST_MARGIN_HALF); + gtk_widget_show (caption); + + /* Prevent the use of some characters in the account title, + * as required by our UI specification: */ + GList *list_prevent = NULL; + list_prevent = g_list_append (list_prevent, "\\"); + list_prevent = g_list_append (list_prevent, "/"); + list_prevent = g_list_append (list_prevent, ":"); + list_prevent = g_list_append (list_prevent, "*"); + list_prevent = g_list_append (list_prevent, "?"); + list_prevent = g_list_append (list_prevent, "\""); /* The UI spec mentions “, but maybe means ", maybe both. */ + list_prevent = g_list_append (list_prevent, "“"); + list_prevent = g_list_append (list_prevent, "<"); + list_prevent = g_list_append (list_prevent, ">"); + list_prevent = g_list_append (list_prevent, "|"); + list_prevent = g_list_append (list_prevent, "^"); + modest_validating_entry_set_unallowed_characters ( + MODEST_VALIDATING_ENTRY (priv->entry_account_title), list_prevent); + g_list_free (list_prevent); + list_prevent = NULL; + modest_validating_entry_set_func(MODEST_VALIDATING_ENTRY(priv->entry_account_title), + modest_utils_on_entry_invalid_character, self); + + /* Set max length as in the UI spec: + * The UI spec seems to want us to show a dialog if we hit the maximum. */ + gtk_entry_set_max_length (GTK_ENTRY (priv->entry_account_title), 64); + modest_validating_entry_set_max_func (MODEST_VALIDATING_ENTRY (priv->entry_account_title), + on_entry_max, self); + + gtk_widget_show (GTK_WIDGET (box)); + + return GTK_WIDGET (box); +} + +static GtkWidget* +create_page_user_details (ModestEasysetupWizardDialog *self) +{ + GtkSizeGroup* sizegroup; + GtkWidget *box; + ModestEasysetupWizardDialogPrivate *priv; + + priv = MODEST_EASYSETUP_WIZARD_DIALOG_GET_PRIVATE(self); + + /* Create a size group to be used by all captions. + * Note that HildonCaption does not create a default size group if we do not specify one. + * We use GTK_SIZE_GROUP_HORIZONTAL, so that the widths are the same. */ + box = gtk_vbox_new (FALSE, MODEST_MARGIN_NONE); + sizegroup = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); + + /* The name widgets: (use auto cap) */ + priv->entry_user_name = GTK_WIDGET (modest_validating_entry_new ()); + hildon_gtk_entry_set_input_mode (GTK_ENTRY (priv->entry_user_name), + HILDON_GTK_INPUT_MODE_FULL | HILDON_GTK_INPUT_MODE_AUTOCAP); + + /* Set max length as in the UI spec: + * The UI spec seems to want us to show a dialog if we hit the maximum. */ + gtk_entry_set_max_length (GTK_ENTRY (priv->entry_user_name), 64); + modest_validating_entry_set_max_func (MODEST_VALIDATING_ENTRY (priv->entry_user_name), + on_entry_max, self); + GtkWidget *caption = create_caption_new_with_asterisk (self, sizegroup, + _("mcen_li_emailsetup_name"), priv->entry_user_name, NULL, HILDON_CAPTION_OPTIONAL); + g_signal_connect(G_OBJECT(priv->entry_user_name), "changed", + G_CALLBACK(on_easysetup_changed), self); + gtk_widget_show (priv->entry_user_name); + gtk_box_pack_start (GTK_BOX (box), caption, FALSE, FALSE, MODEST_MARGIN_HALF); + gtk_widget_show (caption); + + /* Prevent the use of some characters in the name, + * as required by our UI specification: */ + GList *list_prevent = NULL; + list_prevent = g_list_append (list_prevent, "<"); + list_prevent = g_list_append (list_prevent, ">"); + modest_validating_entry_set_unallowed_characters ( + MODEST_VALIDATING_ENTRY (priv->entry_user_name), list_prevent); + modest_validating_entry_set_func(MODEST_VALIDATING_ENTRY(priv->entry_user_name), + modest_utils_on_entry_invalid_character, self); + g_list_free (list_prevent); + + /* The username widgets: */ + priv->entry_user_username = GTK_WIDGET (modest_validating_entry_new ()); + /* Auto-capitalization is the default, so let's turn it off: */ + hildon_gtk_entry_set_input_mode (GTK_ENTRY (priv->entry_user_username), + HILDON_GTK_INPUT_MODE_FULL); + caption = create_caption_new_with_asterisk (self, sizegroup, _("mail_fi_username"), + priv->entry_user_username, + NULL, HILDON_CAPTION_MANDATORY); + gtk_widget_show (priv->entry_user_username); + gtk_box_pack_start (GTK_BOX (box), caption, FALSE, FALSE, MODEST_MARGIN_HALF); + g_signal_connect(G_OBJECT(priv->entry_user_username), "changed", + G_CALLBACK(on_easysetup_changed), self); + gtk_widget_show (caption); + + /* Prevent the use of some characters in the username, + * as required by our UI specification: */ + modest_validating_entry_set_unallowed_characters_whitespace ( + MODEST_VALIDATING_ENTRY (priv->entry_user_username)); + modest_validating_entry_set_func(MODEST_VALIDATING_ENTRY(priv->entry_user_username), + modest_utils_on_entry_invalid_character, self); + + /* Set max length as in the UI spec: + * The UI spec seems to want us to show a dialog if we hit the maximum. */ + gtk_entry_set_max_length (GTK_ENTRY (priv->entry_user_username), 64); + modest_validating_entry_set_max_func (MODEST_VALIDATING_ENTRY (priv->entry_user_username), + on_entry_max, self); + + /* The password widgets: */ + priv->entry_user_password = gtk_entry_new (); + /* Auto-capitalization is the default, so let's turn it off: */ + hildon_gtk_entry_set_input_mode (GTK_ENTRY (priv->entry_user_password), + HILDON_GTK_INPUT_MODE_FULL | HILDON_GTK_INPUT_MODE_INVISIBLE); + gtk_entry_set_visibility (GTK_ENTRY (priv->entry_user_password), FALSE); + /* gtk_entry_set_invisible_char (GTK_ENTRY (priv->entry_user_password), '*'); */ + caption = create_caption_new_with_asterisk (self, sizegroup, + _("mail_fi_password"), priv->entry_user_password, NULL, HILDON_CAPTION_OPTIONAL); + g_signal_connect(G_OBJECT(priv->entry_user_password), "changed", + G_CALLBACK(on_easysetup_changed), self); + gtk_widget_show (priv->entry_user_password); + gtk_box_pack_start (GTK_BOX (box), caption, FALSE, FALSE, MODEST_MARGIN_HALF); + gtk_widget_show (caption); + + /* The email address widgets: */ + priv->entry_user_email = GTK_WIDGET (modest_validating_entry_new ()); + /* Auto-capitalization is the default, so let's turn it off: */ + hildon_gtk_entry_set_input_mode (GTK_ENTRY (priv->entry_user_email), HILDON_GTK_INPUT_MODE_FULL); + caption = create_caption_new_with_asterisk (self, sizegroup, + _("mcen_li_emailsetup_email_address"), priv->entry_user_email, NULL, HILDON_CAPTION_MANDATORY); + gtk_entry_set_text (GTK_ENTRY (priv->entry_user_email), MODEST_EXAMPLE_EMAIL_ADDRESS); /* Default text. */ + gtk_widget_show (priv->entry_user_email); + gtk_box_pack_start (GTK_BOX (box), caption, FALSE, FALSE, MODEST_MARGIN_HALF); + g_signal_connect(G_OBJECT(priv->entry_user_email), "changed", + G_CALLBACK(on_easysetup_changed), self); + gtk_widget_show (caption); + + /* Set max length as in the UI spec: + * The UI spec seems to want us to show a dialog if we hit the maximum. */ + gtk_entry_set_max_length (GTK_ENTRY (priv->entry_user_email), 64); + modest_validating_entry_set_max_func (MODEST_VALIDATING_ENTRY (priv->entry_user_email), + on_entry_max, self); + + + gtk_widget_show (GTK_WIDGET (box)); + + return GTK_WIDGET (box); +} + +static GtkWidget* +create_page_complete_easysetup (ModestEasysetupWizardDialog *self) +{ + GtkWidget *box = gtk_vbox_new (FALSE, MODEST_MARGIN_NONE); + + GtkWidget *label = gtk_label_new(_("mcen_ia_emailsetup_setup_complete")); + gtk_label_set_line_wrap (GTK_LABEL (label), TRUE); + gtk_widget_set_size_request (label, 600, -1); + gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5); + /* The documentation for gtk_label_set_line_wrap() says that we must + * call gtk_widget_set_size_request() with a hard-coded width, + * though I wonder why gtk_label_set_max_width_chars() isn't enough. */ + gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0); + gtk_widget_show (label); + + label = gtk_label_new (_("mcen_ia_easysetup_complete")); + gtk_label_set_line_wrap (GTK_LABEL (label), TRUE); + gtk_widget_set_size_request (label, 600, -1); + + gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5); + gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0); + gtk_widget_show (label); + + gtk_widget_show (GTK_WIDGET (box)); + return GTK_WIDGET (box); +} + +/** Change the caption title for the incoming server, + * as specified in the UI spec: + */ +static void +update_incoming_server_title (ModestEasysetupWizardDialog *self) +{ + ModestEasysetupWizardDialogPrivate* priv; + ModestProtocolType protocol_type; + ModestProtocolRegistry *protocol_registry; + + priv = MODEST_EASYSETUP_WIZARD_DIALOG_GET_PRIVATE(self); + protocol_registry = modest_runtime_get_protocol_registry (); + + protocol_type = easysetup_servertype_combo_box_get_active_servertype ( + EASYSETUP_SERVERTYPE_COMBO_BOX (priv->combo_incoming_servertype)); + + /* This could happen when the combo box has still no active iter */ + if (protocol_type != MODEST_PROTOCOL_REGISTRY_TYPE_INVALID) { + gchar* incomingserver_title; + const gchar *protocol_display_name; + + protocol_display_name = modest_protocol_get_display_name (modest_protocol_registry_get_protocol_by_type (protocol_registry, protocol_type)); + + incomingserver_title = g_strdup_printf(_("mcen_li_emailsetup_servertype"), protocol_display_name); + g_object_set (G_OBJECT (priv->caption_incoming), "label", incomingserver_title, NULL); + g_free(incomingserver_title); + } +} + +/** Change the caption title for the incoming server, + * as specified in the UI spec: + */ +static void +update_incoming_server_security_choices (ModestEasysetupWizardDialog *self) +{ + ModestEasysetupWizardDialogPrivate *priv = MODEST_EASYSETUP_WIZARD_DIALOG_GET_PRIVATE(self); + EasysetupServertypeComboBox *server_type_combo; + ModestProtocolType protocol_type; + ModestSecurityOptionsView *view; + + server_type_combo = + EASYSETUP_SERVERTYPE_COMBO_BOX (priv->combo_incoming_servertype); + protocol_type = + easysetup_servertype_combo_box_get_active_servertype (server_type_combo); + + /* Fill the combo with appropriately titled choices for all + those protocols */ + view = MODEST_SECURITY_OPTIONS_VIEW (priv->incoming_security); + modest_security_options_view_set_server_type (view, protocol_type); +} + +static void +on_combo_servertype_changed(GtkComboBox *combobox, gpointer user_data) +{ + ModestEasysetupWizardDialog *self = MODEST_EASYSETUP_WIZARD_DIALOG (user_data); + ModestEasysetupWizardDialogPrivate *priv = MODEST_EASYSETUP_WIZARD_DIALOG_GET_PRIVATE(self); + EasysetupServertypeComboBox *combo; + ModestProtocolType protocol_type; + + priv->dirty = TRUE; + + /* Update title */ + update_incoming_server_title (self); + + /* Update security options if needed */ + combo = EASYSETUP_SERVERTYPE_COMBO_BOX (combobox); + protocol_type = easysetup_servertype_combo_box_get_active_servertype (combo); + update_incoming_server_security_choices (self); + gtk_widget_show (priv->incoming_security); + + set_default_custom_servernames (self); +} + +static void +on_entry_incoming_servername_changed(GtkEntry *entry, gpointer user_data) +{ + ModestEasysetupWizardDialog *self = MODEST_EASYSETUP_WIZARD_DIALOG (user_data); + ModestEasysetupWizardDialogPrivate *priv = MODEST_EASYSETUP_WIZARD_DIALOG_GET_PRIVATE (self); + priv->dirty = TRUE; + priv->server_changes |= MODEST_EASYSETUP_WIZARD_DIALOG_INCOMING_CHANGED; +} + +static GtkWidget* +create_page_custom_incoming (ModestEasysetupWizardDialog *self) +{ + ModestProtocolRegistry *protocol_registry; + ModestEasysetupWizardDialogPrivate* priv; + GtkWidget *box; + GtkWidget *scrolled_window; + GtkWidget *label; + GtkSizeGroup *sizegroup; + GtkWidget *caption; + + priv = MODEST_EASYSETUP_WIZARD_DIALOG_GET_PRIVATE(self); + protocol_registry = modest_runtime_get_protocol_registry (); + + box = gtk_vbox_new (FALSE, MODEST_MARGIN_NONE); + scrolled_window = gtk_scrolled_window_new (NULL, NULL); + + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window), + GTK_POLICY_NEVER, + GTK_POLICY_AUTOMATIC); + + /* Show note that account type cannot be changed in future: */ + label = gtk_label_new (_("mcen_ia_emailsetup_account_type")); + gtk_label_set_line_wrap (GTK_LABEL (label), TRUE); + gtk_widget_set_size_request (label, 600, -1); + gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0); + gtk_widget_show (label); + + /* Create a size group to be used by all captions. + * Note that HildonCaption does not create a default size group if we do not specify one. + * We use GTK_SIZE_GROUP_HORIZONTAL, so that the widths are the same. */ + sizegroup = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); + + /* The incoming server widgets: */ + priv->combo_incoming_servertype = GTK_WIDGET (easysetup_servertype_combo_box_new (TRUE)); + caption = create_caption_new_with_asterisk (self, sizegroup, + _("mcen_li_emailsetup_type"), priv->combo_incoming_servertype, NULL, HILDON_CAPTION_MANDATORY); + gtk_widget_show (priv->combo_incoming_servertype); + gtk_box_pack_start (GTK_BOX (box), caption, FALSE, FALSE, MODEST_MARGIN_HALF); + gtk_widget_show (caption); + + priv->entry_incomingserver = gtk_entry_new (); + g_signal_connect(G_OBJECT(priv->entry_incomingserver), "changed", G_CALLBACK(on_easysetup_changed), self); + /* Auto-capitalization is the default, so let's turn it off: */ + hildon_gtk_entry_set_input_mode (GTK_ENTRY (priv->entry_incomingserver), HILDON_GTK_INPUT_MODE_FULL); + set_default_custom_servernames (self); + + /* The caption title will be updated in update_incoming_server_title(). + * so this default text will never be seen: */ + /* (Note: Changing the title seems pointless. murrayc) */ + priv->caption_incoming = create_caption_new_with_asterisk (self, sizegroup, + "Incoming Server", + priv->entry_incomingserver, + NULL, HILDON_CAPTION_MANDATORY); + update_incoming_server_title (self); + gtk_widget_show (priv->entry_incomingserver); + gtk_box_pack_start (GTK_BOX (box), priv->caption_incoming, FALSE, FALSE, MODEST_MARGIN_HALF); + gtk_widget_show (priv->caption_incoming); + + /* Change the caption title when the servertype changes, + * as in the UI spec: */ + g_signal_connect (G_OBJECT (priv->combo_incoming_servertype), "changed", + G_CALLBACK (on_combo_servertype_changed), self); + + /* Remember when the servername was changed manually: */ + g_signal_connect (G_OBJECT (priv->entry_incomingserver), "changed", + G_CALLBACK (on_entry_incoming_servername_changed), self); + + /* The secure connection widgets. These are only valid for + protocols with security */ + priv->incoming_security = + modest_maemo_security_options_view_new (MODEST_SECURITY_OPTIONS_INCOMING, + FALSE, sizegroup); + gtk_box_pack_start (GTK_BOX (box), priv->incoming_security, + FALSE, FALSE, MODEST_MARGIN_HALF); + + /* Set default selection */ + easysetup_servertype_combo_box_set_active_servertype ( + EASYSETUP_SERVERTYPE_COMBO_BOX (priv->combo_incoming_servertype), + MODEST_PROTOCOLS_STORE_POP); + + gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scrolled_window), box); + gtk_container_set_focus_vadjustment (GTK_CONTAINER (box), + gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (scrolled_window))); + gtk_widget_show (GTK_WIDGET (box)); + gtk_widget_show (scrolled_window); + + return GTK_WIDGET (scrolled_window); +} + +static void +on_toggle_button_changed (GtkToggleButton *togglebutton, gpointer user_data) +{ + GtkWidget *widget = GTK_WIDGET (user_data); + + /* Enable the widget only if the toggle button is active: */ + const gboolean enable = gtk_toggle_button_get_active (togglebutton); + gtk_widget_set_sensitive (widget, enable); +} + +/* Make the sensitivity of a widget depend on a toggle button. + */ +static void +enable_widget_for_togglebutton (GtkWidget *widget, GtkToggleButton* button) +{ + g_signal_connect (G_OBJECT (button), "toggled", + G_CALLBACK (on_toggle_button_changed), widget); + + /* Set the starting sensitivity: */ + on_toggle_button_changed (button, widget); +} + +static void +on_button_outgoing_smtp_servers (GtkButton *button, gpointer user_data) +{ + ModestEasysetupWizardDialog * self = MODEST_EASYSETUP_WIZARD_DIALOG (user_data); + ModestEasysetupWizardDialogPrivate* priv = MODEST_EASYSETUP_WIZARD_DIALOG_GET_PRIVATE(self); + GtkWidget *specific_window; + + /* We set dirty here because setting it depending on the connection specific dialog + seems overkill */ + priv->dirty = TRUE; + + /* Create the window, if necessary: */ + specific_window = (GtkWidget *) modest_connection_specific_smtp_window_new (); + modest_connection_specific_smtp_window_fill_with_connections (MODEST_CONNECTION_SPECIFIC_SMTP_WINDOW (specific_window), priv->account_manager); + + /* Show the window */ + modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), GTK_WINDOW (specific_window)); + gtk_widget_show (specific_window); +} + +static void +on_entry_outgoing_servername_changed (GtkEntry *entry, gpointer user_data) +{ + ModestEasysetupWizardDialog *self = MODEST_EASYSETUP_WIZARD_DIALOG (user_data); + ModestEasysetupWizardDialogPrivate *priv = MODEST_EASYSETUP_WIZARD_DIALOG_GET_PRIVATE (self); + priv->server_changes |= MODEST_EASYSETUP_WIZARD_DIALOG_OUTGOING_CHANGED; +} + +static GtkWidget* +create_page_custom_outgoing (ModestEasysetupWizardDialog *self) +{ + ModestEasysetupWizardDialogPrivate *priv; + GtkWidget *box = gtk_vbox_new (FALSE, MODEST_MARGIN_NONE); + + /* Create a size group to be used by all captions. + * Note that HildonCaption does not create a default size group if we do not specify one. + * We use GTK_SIZE_GROUP_HORIZONTAL, so that the widths are the same. */ + GtkSizeGroup *sizegroup = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); + + /* The outgoing server widgets: */ + priv = MODEST_EASYSETUP_WIZARD_DIALOG_GET_PRIVATE (self); + priv->entry_outgoingserver = gtk_entry_new (); + g_signal_connect (G_OBJECT (priv->entry_outgoingserver), "changed", + G_CALLBACK (on_easysetup_changed), self); + /* Auto-capitalization is the default, so let's turn it off: */ + hildon_gtk_entry_set_input_mode (GTK_ENTRY (priv->entry_outgoingserver), HILDON_GTK_INPUT_MODE_FULL); + GtkWidget *caption = create_caption_new_with_asterisk (self, sizegroup, + _("mcen_li_emailsetup_smtp"), priv->entry_outgoingserver, NULL, HILDON_CAPTION_OPTIONAL); + gtk_widget_show (priv->entry_outgoingserver); + gtk_box_pack_start (GTK_BOX (box), caption, FALSE, FALSE, MODEST_MARGIN_HALF); + gtk_widget_show (caption); + set_default_custom_servernames (self); + + /* The secure connection widgets. These are only valid for + protocols with security */ + priv->outgoing_security = + modest_maemo_security_options_view_new (MODEST_SECURITY_OPTIONS_OUTGOING, + FALSE, sizegroup); + gtk_box_pack_start (GTK_BOX (box), priv->outgoing_security, + FALSE, FALSE, MODEST_MARGIN_HALF); + gtk_widget_show (priv->outgoing_security); + + GtkWidget *separator = gtk_hseparator_new (); + gtk_box_pack_start (GTK_BOX (box), separator, FALSE, FALSE, MODEST_MARGIN_HALF); + gtk_widget_show (separator); + + /* connection-specific checkbox: */ + priv->checkbox_outgoing_smtp_specific = gtk_check_button_new (); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->checkbox_outgoing_smtp_specific), + FALSE); + g_signal_connect (G_OBJECT (priv->checkbox_outgoing_smtp_specific), "toggled", + G_CALLBACK (on_easysetup_changed), self); + + caption = hildon_caption_new (sizegroup, _("mcen_fi_advsetup_connection_smtp"), + priv->checkbox_outgoing_smtp_specific, NULL, HILDON_CAPTION_OPTIONAL); + gtk_widget_show (priv->checkbox_outgoing_smtp_specific); + gtk_box_pack_start (GTK_BOX (box), caption, FALSE, FALSE, MODEST_MARGIN_HALF); + gtk_widget_show (caption); + + /* Connection-specific SMTP-Severs Edit button: */ + priv->button_outgoing_smtp_servers = gtk_button_new_with_label (_("mcen_bd_edit")); + caption = hildon_caption_new (sizegroup, _("mcen_fi_advsetup_optional_smtp"), + priv->button_outgoing_smtp_servers, NULL, HILDON_CAPTION_OPTIONAL); + hildon_caption_set_child_expand (HILDON_CAPTION (caption), FALSE); + gtk_widget_show (priv->button_outgoing_smtp_servers); + gtk_box_pack_start (GTK_BOX (box), caption, FALSE, FALSE, MODEST_MARGIN_HALF); + gtk_widget_show (caption); + + /* Only enable the button when the checkbox is checked: */ + enable_widget_for_togglebutton (priv->button_outgoing_smtp_servers, + GTK_TOGGLE_BUTTON (priv->checkbox_outgoing_smtp_specific)); + + g_signal_connect (G_OBJECT (priv->button_outgoing_smtp_servers), "clicked", + G_CALLBACK (on_button_outgoing_smtp_servers), self); + + g_signal_connect (G_OBJECT (priv->entry_outgoingserver), "changed", + G_CALLBACK (on_entry_outgoing_servername_changed), self); + + + gtk_widget_show (GTK_WIDGET (box)); + + return GTK_WIDGET (box); +} + +static gboolean +show_advanced_edit(gpointer user_data) +{ + ModestEasysetupWizardDialog * self = MODEST_EASYSETUP_WIZARD_DIALOG (user_data); + ModestEasysetupWizardDialogPrivate *priv = MODEST_EASYSETUP_WIZARD_DIALOG_GET_PRIVATE (self); + gint response; + + /* Show the Account Settings window: */ + ModestAccountSettingsDialog *dialog = modest_default_account_settings_dialog_new (); + if (priv->pending_load_settings) { + save_to_settings (self); + priv->pending_load_settings = FALSE; + } + modest_account_settings_dialog_load_settings (dialog, priv->settings); + + modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), GTK_WINDOW (dialog)); + + response = gtk_dialog_run (GTK_DIALOG (dialog)); + + gtk_widget_destroy (GTK_WIDGET (dialog)); + + return FALSE; /* Do not call this timeout callback again. */ +} + +static void +on_button_edit_advanced_settings (GtkButton *button, gpointer user_data) +{ + ModestEasysetupWizardDialog * self = MODEST_EASYSETUP_WIZARD_DIALOG (user_data); + + /* Show the Account Settings window: */ + show_advanced_edit(self); +} +static GtkWidget* +create_page_complete_custom (ModestEasysetupWizardDialog *self) +{ + GtkWidget *box = gtk_vbox_new (FALSE, MODEST_MARGIN_NONE); + GtkWidget *label = gtk_label_new(_("mcen_ia_emailsetup_setup_complete")); + GtkWidget *button_edit = gtk_button_new_with_label (_("mcen_bd_edit")); + gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5); + gtk_widget_set_size_request (label, 600, -1); + gtk_label_set_line_wrap (GTK_LABEL (label), TRUE); + gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0); + gtk_widget_show (label); + + label = gtk_label_new (_("mcen_ia_customsetup_complete")); + gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5); + gtk_widget_set_size_request (label, 600, -1); + gtk_label_set_line_wrap (GTK_LABEL (label), TRUE); + gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0); + gtk_widget_show (label); + + GtkWidget *caption = hildon_caption_new (NULL, _("mcen_fi_advanced_settings"), + button_edit, NULL, HILDON_CAPTION_OPTIONAL); + hildon_caption_set_child_expand (HILDON_CAPTION (caption), FALSE); + gtk_widget_show (button_edit); + gtk_box_pack_start (GTK_BOX (box), caption, FALSE, FALSE, MODEST_MARGIN_HALF); + gtk_widget_show (caption); + + g_signal_connect (G_OBJECT (button_edit), "clicked", + G_CALLBACK (on_button_edit_advanced_settings), self); + + gtk_widget_show (GTK_WIDGET (box)); + return GTK_WIDGET (box); +} + + +/* + */ +static void +on_response (ModestWizardDialog *wizard_dialog, + gint response_id, + gpointer user_data) +{ + ModestEasysetupWizardDialog *self = MODEST_EASYSETUP_WIZARD_DIALOG (wizard_dialog); + + invoke_enable_buttons_vfunc (self); +} + +static void +on_response_before (ModestWizardDialog *wizard_dialog, + gint response_id, + gpointer user_data) +{ + ModestEasysetupWizardDialog *self = MODEST_EASYSETUP_WIZARD_DIALOG (wizard_dialog); + ModestEasysetupWizardDialogPrivate *priv = MODEST_EASYSETUP_WIZARD_DIALOG_GET_PRIVATE(wizard_dialog); + if (response_id == GTK_RESPONSE_CANCEL) { + /* This is mostly copied from + * src/maemo/modest-account-settings-dialog.c */ + if (priv->dirty) { + GtkDialog *dialog = GTK_DIALOG (hildon_note_new_confirmation (GTK_WINDOW (self), + _("imum_nc_wizard_confirm_lose_changes"))); + /* TODO: These button names will be ambiguous, and not + * specified in the UI specification. */ + + const gint dialog_response = gtk_dialog_run (dialog); + gtk_widget_destroy (GTK_WIDGET (dialog)); + + if (dialog_response != GTK_RESPONSE_OK) { + /* Don't let the dialog close */ + g_signal_stop_emission_by_name (wizard_dialog, "response"); + } + } + } +} + +typedef struct IdleData { + ModestEasysetupWizardDialog *dialog; + ModestPresets *presets; +} IdleData; + +static gboolean +presets_idle (gpointer userdata) +{ + IdleData *idle_data = (IdleData *) userdata; + ModestEasysetupWizardDialog *self = MODEST_EASYSETUP_WIZARD_DIALOG (idle_data->dialog); + ModestEasysetupWizardDialogPrivate *priv = MODEST_EASYSETUP_WIZARD_DIALOG_GET_PRIVATE (self); + + g_assert (idle_data->presets); + + gdk_threads_enter (); + + priv->presets = idle_data->presets; + + if (MODEST_EASYSETUP_IS_COUNTRY_COMBO_BOX (priv->combo_account_country)) { +/* gint mcc = get_default_country_code(); */ + gint mcc; + /* Fill the combo in an idle call, as it takes a lot of time */ + easysetup_country_combo_box_load_data( + MODEST_EASYSETUP_COUNTRY_COMBO_BOX (priv->combo_account_country)); +/* easysetup_country_combo_box_set_active_country_mcc ( */ +/* MODEST_EASYSETUP_COUNTRY_COMBO_BOX (priv->combo_account_country), mcc); */ + easysetup_country_combo_box_set_active_country_locale ( + MODEST_EASYSETUP_COUNTRY_COMBO_BOX (priv->combo_account_country)); + mcc = easysetup_country_combo_box_get_active_country_mcc ( + MODEST_EASYSETUP_COUNTRY_COMBO_BOX (priv->combo_account_country)); + easysetup_provider_combo_box_fill ( + EASYSETUP_PROVIDER_COMBO_BOX (priv->combo_account_serviceprovider), + priv->presets, mcc); + } + + priv->dirty = FALSE; + + g_object_unref (idle_data->dialog); + g_free (idle_data); + + gdk_threads_leave (); + + return FALSE; +} + +static gpointer +presets_loader (gpointer userdata) +{ + ModestEasysetupWizardDialog *self = MODEST_EASYSETUP_WIZARD_DIALOG (userdata); + ModestPresets *presets = NULL; + IdleData *idle_data; + + const gchar* path = NULL; + const gchar* path1 = MODEST_PROVIDER_DATA_FILE; + const gchar* path2 = MODEST_MAEMO_PROVIDER_DATA_FILE; + + if (access(path1, R_OK) == 0) + path = path1; + else if (access(path2, R_OK) == 0) + path = path2; + else { + g_warning ("%s: neither '%s' nor '%s' is a readable provider data file", + __FUNCTION__, path1, path2); + return NULL; + } + + presets = modest_presets_new (path); + if (!presets) { + g_warning ("%s: failed to parse '%s'", __FUNCTION__, path); + return NULL; + } + + idle_data = g_new0 (IdleData, 1); + idle_data->dialog = self; + idle_data->presets = presets; + + g_idle_add (presets_idle, idle_data); + + return NULL; +} + +static void +modest_easysetup_wizard_dialog_append_page (GtkNotebook *notebook, + GtkWidget *page, + const gchar *label) +{ + gint index; + /* Append page and set attributes */ + index = gtk_notebook_append_page (notebook, page, gtk_label_new (label)); + gtk_container_child_set (GTK_CONTAINER (notebook), page, + "tab-expand", TRUE, "tab-fill", TRUE, + NULL); + gtk_widget_show (page); +} + +static void +init_user_page (ModestEasysetupWizardDialogPrivate *priv) +{ + priv->page_user_details = NULL; + priv->entry_user_name = NULL; + priv->entry_user_username = NULL; + priv->entry_user_password = NULL; + priv->entry_user_email = NULL; +} + +static void +init_incoming_page (ModestEasysetupWizardDialogPrivate *priv) +{ + priv->page_custom_incoming = NULL; + priv->combo_incoming_servertype = NULL; + priv->caption_incoming = NULL; + priv->entry_incomingserver = NULL; + priv->entry_user_email = NULL; + priv->incoming_security = NULL; +} + +static void +init_outgoing_page (ModestEasysetupWizardDialogPrivate *priv) +{ + priv->page_custom_outgoing = NULL; + priv->entry_outgoingserver = NULL; + priv->combo_outgoing_security = NULL; + priv->combo_outgoing_auth = NULL; + priv->checkbox_outgoing_smtp_specific = NULL; + priv->button_outgoing_smtp_servers = NULL; + priv->outgoing_security = NULL; +} + +static void +modest_easysetup_wizard_dialog_init (ModestEasysetupWizardDialog *self) +{ + gtk_container_set_border_width (GTK_CONTAINER (self), MODEST_MARGIN_HALF); + + /* Create the notebook to be used by the ModestWizardDialog base class: + * Each page of the notebook will be a page of the wizard: */ + GtkNotebook *notebook = GTK_NOTEBOOK (gtk_notebook_new()); + + /* Set the notebook used by the ModestWizardDialog base class: */ + g_object_set (G_OBJECT(self), "wizard-notebook", notebook, NULL); + + /* Set the wizard title: + * The actual window title will be a combination of this and the page's tab label title. */ + g_object_set (G_OBJECT(self), "wizard-name", _("mcen_ti_emailsetup"), NULL); + + /* Read in the information about known service providers: */ + ModestEasysetupWizardDialogPrivate *priv = MODEST_EASYSETUP_WIZARD_DIALOG_GET_PRIVATE (self); + + /* The server fields did not have been manually changed yet */ + priv->server_changes = 0; + priv->pending_load_settings = TRUE; + + /* Get the account manager object, + * so we can check for existing accounts, + * and create new accounts: */ + priv->account_manager = modest_runtime_get_account_mgr (); + g_object_ref (priv->account_manager); + + /* Initialize fields */ + priv->page_welcome = create_page_welcome (self); + priv->page_account_details = create_page_account_details (self); + + init_user_page (priv); + init_incoming_page (priv); + init_outgoing_page (priv); + + priv->page_complete_easysetup = NULL; + priv->page_complete_customsetup = NULL; + priv->last_plugin_protocol_selected = MODEST_PROTOCOL_REGISTRY_TYPE_INVALID; + priv->missing_data_signals = NULL; + + /* Add the common pages */ + modest_easysetup_wizard_dialog_append_page (notebook, priv->page_welcome, + _("mcen_ti_emailsetup_welcome")); + modest_easysetup_wizard_dialog_append_page (notebook, priv->page_account_details, + _("mcen_ti_accountdetails")); + + /* Connect to the dialog's response signal so we can enable/disable buttons + * for the newly-selected page, because the prev/next buttons cause response to be emitted. + * Note that we use g_signal_connect_after() instead of g_signal_connect() + * so that we can be enable/disable after ModestWizardDialog has done its own + * enabling/disabling of buttons. + * + * HOWEVER, this doesn't work because ModestWizardDialog's response signal handler + * does g_signal_stop_emission_by_name(), stopping our signal handler from running. + * + * It's not enough to connect to the notebook's switch-page signal, because + * ModestWizardDialog's "response" signal handler enables the buttons itself, + * _after_ switching the page (understandably). + * (Note that if we had, if we used g_signal_connect() instead of g_signal_connect_after() + * then gtk_notebook_get_current_page() would return an incorrect value.) + */ + g_signal_connect_after (G_OBJECT (self), "response", + G_CALLBACK (on_response), self); + + /* This is to show a confirmation dialog when the user hits cancel */ + g_signal_connect (G_OBJECT (self), "response", + G_CALLBACK (on_response_before), self); + + g_signal_connect (G_OBJECT (self), "delete-event", + G_CALLBACK (on_delete_event), self); + + /* Reset dirty, because there was no user input until now */ + priv->dirty = FALSE; + + /* When this window is shown, hibernation should not be possible, + * because there is no sensible way to save the state: */ + modest_window_mgr_prevent_hibernation_while_window_is_shown ( + modest_runtime_get_window_mgr (), GTK_WINDOW (self)); + + /* Load provider presets */ + g_object_ref (self); + g_thread_create (presets_loader, self, FALSE, NULL); + + hildon_help_dialog_help_enable (GTK_DIALOG(self), "applications_email_wizardwelcome", + modest_maemo_utils_get_osso_context()); + + priv->settings = modest_account_settings_new (); +} + +ModestEasysetupWizardDialog* +modest_easysetup_wizard_dialog_new (void) +{ + + return g_object_new (MODEST_TYPE_EASYSETUP_WIZARD_DIALOG, NULL); +} + +static void +create_subsequent_customsetup_pages (ModestEasysetupWizardDialog *self) +{ + ModestEasysetupWizardDialogPrivate *priv; + GtkNotebook *notebook = NULL; + + g_object_get (self, "wizard-notebook", ¬ebook, NULL); + g_assert(notebook); + + priv = MODEST_EASYSETUP_WIZARD_DIALOG_GET_PRIVATE (self); + + if (!priv->page_user_details) { + priv->page_user_details = create_page_user_details (self); + } + + /* Create the custom pages: */ + if(!(priv->page_custom_incoming)) { + priv->page_custom_incoming = create_page_custom_incoming (self); + } + + /* TODO: only if needed */ + if(!(priv->page_custom_outgoing)) { + priv->page_custom_outgoing = create_page_custom_outgoing (self); + } + + if(!(priv->page_complete_customsetup)) { + priv->page_complete_customsetup = create_page_complete_custom (self); + } + + if (!gtk_widget_get_parent (GTK_WIDGET (priv->page_user_details))) + modest_easysetup_wizard_dialog_append_page (notebook, priv->page_user_details, + _("mcen_ti_emailsetup_userdetails")); + + if (!gtk_widget_get_parent (GTK_WIDGET (priv->page_custom_incoming))) + modest_easysetup_wizard_dialog_append_page (notebook, priv->page_custom_incoming, + _("mcen_ti_emailsetup_incomingdetails")); + + if (!gtk_widget_get_parent (GTK_WIDGET (priv->page_custom_outgoing))) + modest_easysetup_wizard_dialog_append_page (notebook, priv->page_custom_outgoing, + _("mcen_ti_emailsetup_outgoingdetails")); + + if (!gtk_widget_get_parent (GTK_WIDGET (priv->page_complete_customsetup))) + modest_easysetup_wizard_dialog_append_page (notebook, priv->page_complete_customsetup, + _("mcen_ti_emailsetup_complete")); + + /* This is unnecessary with GTK+ 2.10: */ + modest_wizard_dialog_force_title_update (MODEST_WIZARD_DIALOG(self)); +} + +static void +create_subsequent_easysetup_pages (ModestEasysetupWizardDialog *self) +{ + ModestEasysetupWizardDialogPrivate *priv; + GtkNotebook *notebook = NULL; + + g_object_get (self, "wizard-notebook", ¬ebook, NULL); + g_assert(notebook); + + /* Create the easysetup-specific pages: */ + priv = MODEST_EASYSETUP_WIZARD_DIALOG_GET_PRIVATE (self); + if(!priv->page_complete_easysetup) + priv->page_complete_easysetup = create_page_complete_easysetup (self); + + if (!gtk_widget_get_parent (GTK_WIDGET (priv->page_complete_easysetup))) + modest_easysetup_wizard_dialog_append_page (notebook, priv->page_complete_easysetup, + _("mcen_ti_emailsetup_complete")); + + /* This is unnecessary with GTK+ 2.10: */ + modest_wizard_dialog_force_title_update (MODEST_WIZARD_DIALOG(self)); +} + +/* */ +static void +remove_non_common_tabs (GtkNotebook *notebook, + gboolean remove_user_details) +{ + gint starting_tab; + /* The first 2 tabs are the common ones (welcome tab and the + providers tab), so we always remove starting from the + end */ + + starting_tab = (remove_user_details) ? 2 : 3; + while (gtk_notebook_get_n_pages (notebook) > starting_tab) + gtk_notebook_remove_page (notebook, -1); +} + +static void +on_missing_mandatory_data (ModestAccountProtocol *protocol, + gboolean missing, + gpointer user_data) +{ + real_enable_buttons (MODEST_WIZARD_DIALOG (user_data), !missing); +} + +/* After the user details page, + * the following pages depend on whether "Other" was chosen + * in the provider combobox on the account page + */ +static void +create_subsequent_pages (ModestEasysetupWizardDialog *self) +{ + ModestEasysetupWizardDialogPrivate *priv; + EasysetupProviderComboBox *combo; + EasysetupProviderComboBoxIdType id_type; + GtkNotebook *notebook; + + priv = MODEST_EASYSETUP_WIZARD_DIALOG_GET_PRIVATE (self); + combo = EASYSETUP_PROVIDER_COMBO_BOX (priv->combo_account_serviceprovider); + id_type = easysetup_provider_combo_box_get_active_id_type (combo); + g_object_get (self, "wizard-notebook", ¬ebook, NULL); + + if (id_type == EASYSETUP_PROVIDER_COMBO_BOX_ID_OTHER) { + /* "Other..." was selected: */ + + /* If we come from a rollbacked easysetup */ + if (priv->page_complete_easysetup) { + remove_non_common_tabs (notebook, FALSE); + priv->page_complete_easysetup = NULL; + } + + /* If we come from a rollbacked plugin protocol setup */ + if (priv->last_plugin_protocol_selected != MODEST_PROTOCOL_REGISTRY_TYPE_INVALID) { + remove_non_common_tabs (notebook, TRUE); + priv->last_plugin_protocol_selected = MODEST_PROTOCOL_REGISTRY_TYPE_INVALID; + modest_signal_mgr_disconnect_all_and_destroy (priv->missing_data_signals); + priv->missing_data_signals = NULL; + } + + create_subsequent_customsetup_pages (self); + } else { + /* If we come from a rollbacked custom setup */ + if (priv->page_custom_incoming) { + remove_non_common_tabs (notebook, TRUE); + init_user_page (priv); + init_incoming_page (priv); + init_outgoing_page (priv); + init_user_page (priv); + priv->page_complete_customsetup = NULL; + } + + /* It's a pluggable protocol and not a provider with presets */ + if (id_type == EASYSETUP_PROVIDER_COMBO_BOX_ID_PLUGIN_PROTOCOL) { + ModestProtocol *protocol; + gchar *proto_name; + ModestProtocolType proto_type; + + + /* If we come from a rollbacked easy setup */ + if (priv->last_plugin_protocol_selected == + MODEST_PROTOCOL_REGISTRY_TYPE_INVALID && + priv->page_complete_easysetup) { + remove_non_common_tabs (notebook, TRUE); + init_user_page (priv); + priv->page_complete_easysetup = NULL; + } + + proto_name = easysetup_provider_combo_box_get_active_provider_id (combo); + protocol = modest_protocol_registry_get_protocol_by_name (modest_runtime_get_protocol_registry (), + MODEST_PROTOCOL_REGISTRY_PROVIDER_PROTOCOLS, + proto_name); + proto_type = modest_protocol_get_type_id (protocol); + + if (protocol && MODEST_IS_ACCOUNT_PROTOCOL (protocol) && + proto_type != priv->last_plugin_protocol_selected) { + ModestPairList *tabs; + GSList *tmp; + + /* Remember the last selected plugin protocol */ + priv->last_plugin_protocol_selected = proto_type; + + /* Get tabs */ + tabs = modest_account_protocol_get_easysetupwizard_tabs (MODEST_ACCOUNT_PROTOCOL (protocol)); + tmp = (GSList *) tabs; + while (tmp) { + ModestPair *pair = (ModestPair *) tmp->data; + modest_easysetup_wizard_dialog_append_page (notebook, + GTK_WIDGET (pair->second), + (const gchar *) pair->first); + + /* Connect signals */ + priv->missing_data_signals = + modest_signal_mgr_connect (priv->missing_data_signals, + G_OBJECT (pair->second), + "missing-mandatory-data", + G_CALLBACK (on_missing_mandatory_data), + self); + + g_free (pair->first); + tmp = g_slist_next (tmp); + /* Critical: if you don't show the page then the dialog will ignore it */ +/* gtk_widget_show (GTK_WIDGET (pair->second)); */ + } + modest_pair_list_free (tabs); + } + g_free (proto_name); + } else { + if (priv->last_plugin_protocol_selected != + MODEST_PROTOCOL_REGISTRY_TYPE_INVALID) { + remove_non_common_tabs (notebook, TRUE); + init_user_page (priv); + priv->page_complete_easysetup = NULL; + priv->last_plugin_protocol_selected = MODEST_PROTOCOL_REGISTRY_TYPE_INVALID; + modest_signal_mgr_disconnect_all_and_destroy (priv->missing_data_signals); + priv->missing_data_signals = NULL; + } + if (!priv->page_user_details) { + priv->page_user_details = create_page_user_details (self); + modest_easysetup_wizard_dialog_append_page (notebook, + priv->page_user_details, + _("mcen_ti_emailsetup_userdetails")); + } + } + + /* Create the easysetup pages: */ + create_subsequent_easysetup_pages (self); + } +} + + +static gchar* +util_get_default_servername_from_email_address (const gchar* email_address, ModestProtocolType protocol_type) +{ + const gchar* hostname = NULL; + gchar* at; + gchar* domain; + ModestProtocolRegistry *protocol_registry; + ModestProtocol *protocol; + + if (!email_address) + return NULL; + + at = g_utf8_strchr (email_address, -1, '@'); + if (!at || (g_utf8_strlen (at, -1) < 2)) + return NULL; + + domain = g_utf8_next_char (at); + if(!domain) + return NULL; + + protocol_registry = modest_runtime_get_protocol_registry (); + protocol = modest_protocol_registry_get_protocol_by_type (protocol_registry, protocol_type); + + if (modest_protocol_registry_protocol_type_has_tag (protocol_registry, protocol_type, MODEST_PROTOCOL_REGISTRY_REMOTE_STORE_PROTOCOLS) || + modest_protocol_registry_protocol_type_has_tag (protocol_registry, protocol_type, MODEST_PROTOCOL_REGISTRY_TRANSPORT_PROTOCOLS)) { + hostname = modest_protocol_get_name (protocol); + } + + if (!hostname) + return NULL; + + return g_strdup_printf ("%s.%s", hostname, domain); +} + +static void +set_default_custom_servernames (ModestEasysetupWizardDialog *self) +{ + ModestEasysetupWizardDialogPrivate *priv = MODEST_EASYSETUP_WIZARD_DIALOG_GET_PRIVATE(self); + + if (!priv->entry_incomingserver) + return; + + /* Set a default domain for the server, based on the email address, + * if no server name was already specified. + */ + if (priv->entry_user_email + && ((priv->server_changes & MODEST_EASYSETUP_WIZARD_DIALOG_INCOMING_CHANGED) == 0)) { + const ModestProtocolType protocol_type = easysetup_servertype_combo_box_get_active_servertype ( + EASYSETUP_SERVERTYPE_COMBO_BOX (priv->combo_incoming_servertype)); + + /* This could happen when the combo box has still no active iter */ + if (protocol_type != MODEST_PROTOCOL_REGISTRY_TYPE_INVALID) { + const gchar* email_address = gtk_entry_get_text (GTK_ENTRY(priv->entry_user_email)); + gchar* servername = util_get_default_servername_from_email_address (email_address, + protocol_type); + + /* Do not set the INCOMING_CHANGED flag because of this edit */ + g_signal_handlers_block_by_func (G_OBJECT (priv->entry_incomingserver), G_CALLBACK (on_entry_incoming_servername_changed), self); + gtk_entry_set_text (GTK_ENTRY (priv->entry_incomingserver), servername); + g_signal_handlers_unblock_by_func (G_OBJECT (priv->entry_incomingserver), G_CALLBACK (on_entry_incoming_servername_changed), self); + + g_free (servername); + } + } + + /* Set a default domain for the server, based on the email address, + * if no server name was already specified. + */ + if (!priv->entry_outgoingserver) + return; + + if (priv->entry_user_email + && ((priv->server_changes & MODEST_EASYSETUP_WIZARD_DIALOG_OUTGOING_CHANGED) == 0)) { + const gchar* email_address = gtk_entry_get_text (GTK_ENTRY(priv->entry_user_email)); + + gchar* servername = util_get_default_servername_from_email_address (email_address, MODEST_PROTOCOLS_TRANSPORT_SMTP); + + /* Do not set the OUTGOING_CHANGED flag because of this edit */ + g_signal_handlers_block_by_func (G_OBJECT (priv->entry_outgoingserver), G_CALLBACK (on_entry_outgoing_servername_changed), self); + gtk_entry_set_text (GTK_ENTRY (priv->entry_outgoingserver), servername); + g_signal_handlers_unblock_by_func (G_OBJECT (priv->entry_outgoingserver), G_CALLBACK (on_entry_outgoing_servername_changed), self); + + g_free (servername); + } +} + +static gchar* +get_entered_account_title (ModestEasysetupWizardDialog *self) +{ + ModestEasysetupWizardDialogPrivate *priv; + const gchar* account_title; + + priv = MODEST_EASYSETUP_WIZARD_DIALOG_GET_PRIVATE(self); + account_title = gtk_entry_get_text (GTK_ENTRY (priv->entry_account_title)); + + if (!account_title || (g_utf8_strlen (account_title, -1) == 0)) { + return NULL; + } else { + /* Strip it of whitespace at the start and end: */ + gchar *result = g_strdup (account_title); + result = g_strstrip (result); + + if (!result) + return NULL; + + if (g_utf8_strlen (result, -1) == 0) { + g_free (result); + return NULL; + } + + return result; + } +} + +static gboolean +on_before_next (ModestWizardDialog *dialog, GtkWidget *current_page, GtkWidget *next_page) +{ + ModestEasysetupWizardDialog *self = MODEST_EASYSETUP_WIZARD_DIALOG (dialog); + ModestEasysetupWizardDialogPrivate *priv = MODEST_EASYSETUP_WIZARD_DIALOG_GET_PRIVATE (self); + ModestProtocolRegistry *protocol_registry; + + protocol_registry = modest_runtime_get_protocol_registry (); + + /* if are browsing pages previous to the last one, then we have pending settings in + * this wizard */ + if (next_page != NULL) + priv->pending_load_settings = TRUE; + + /* Do extra validation that couldn't be done for every key press, + * either because it was too slow, + * or because it requires interaction: + */ + if (current_page == priv->page_account_details) { + /* Check that the title is not already in use: */ + gchar* account_title = get_entered_account_title (self); + if (!account_title) + return FALSE; + + /* Aavoid a clash with an existing display name: */ + const gboolean name_in_use = modest_account_mgr_account_with_display_name_exists ( + priv->account_manager, account_title); + + if (name_in_use) { + /* Warn the user via a dialog: */ + hildon_banner_show_information(NULL, NULL, _("mail_ib_account_name_already_existing")); + + return FALSE; + } + + /* Make sure that the subsequent pages are appropriate for the provider choice. */ + create_subsequent_pages (self); + + } else if (current_page == priv->page_user_details) { + /* Check that the email address is valud: */ + const gchar* email_address = gtk_entry_get_text (GTK_ENTRY (priv->entry_user_email)); + if ((!email_address) || (g_utf8_strlen (email_address, -1) == 0)) + return FALSE; + + if (!modest_text_utils_validate_email_address (email_address, NULL)) { + /* Warn the user via a dialog: */ + hildon_banner_show_information (NULL, NULL, _("mcen_ib_invalid_email")); + + /* Return focus to the email address entry: */ + gtk_widget_grab_focus (priv->entry_user_email); + gtk_editable_select_region (GTK_EDITABLE (priv->entry_user_email), 0, -1); + + return FALSE; + } + } + + if (next_page == priv->page_custom_incoming) { + set_default_custom_servernames (self); + } else if (next_page == priv->page_custom_outgoing) { + set_default_custom_servernames (self); + + /* Check if the server supports secure authentication */ +/* if (modest_security_options_view_auth_check (security_options)) */ +/* if (!check_has_supported_auth_methods (self)) */ +/* return FALSE; */ + gtk_widget_show (priv->outgoing_security); + } + + /* If this is the last page, and this is a click on Finish, + * then attempt to create the dialog. + */ + if(!next_page && + current_page != priv->page_account_details) /* This is NULL when this is a click on Finish. */ + { + if (priv->pending_load_settings) { + save_to_settings (self); + } + + /* We check if there's already another account with the same configuration */ + if (modest_account_mgr_check_already_configured_account (priv->account_manager, priv->settings)) { + modest_platform_information_banner (NULL, NULL, _("mail_ib_setting_failed")); + return FALSE; + } + + modest_account_mgr_add_account_from_settings (priv->account_manager, priv->settings); + } + + + return TRUE; +} + +static gboolean entry_is_empty (GtkWidget *entry) +{ + if (!entry) + return FALSE; + + const gchar* text = gtk_entry_get_text (GTK_ENTRY (entry)); + if ((!text) || (g_utf8_strlen (text, -1) == 0)) + return TRUE; + else { + /* Strip it of whitespace at the start and end: */ + gchar *stripped = g_strdup (text); + stripped = g_strstrip (stripped); + + if (!stripped) + return TRUE; + + const gboolean result = (g_utf8_strlen (stripped, -1) == 0); + + g_free (stripped); + return result; + } +} + +static void +real_enable_buttons (ModestWizardDialog *dialog, gboolean enable_next) +{ + GtkNotebook *notebook = NULL; + gboolean is_finish_tab; + GtkWidget *current; + ModestEasysetupWizardDialogPrivate *priv; + + /* Get data */ + priv = MODEST_EASYSETUP_WIZARD_DIALOG_GET_PRIVATE (dialog); + g_object_get (dialog, "wizard-notebook", ¬ebook, NULL); + + /* Disable the Finish button until we are on the last page, + * because HildonWizardDialog enables this for all but the + * first page */ + current = gtk_notebook_get_nth_page (notebook, gtk_notebook_get_current_page (notebook)); + is_finish_tab = ((current == priv->page_complete_easysetup) || + (current == priv->page_complete_customsetup)); + + if (is_finish_tab) { + /* Disable Next on the last page no matter what the + argument say */ + enable_next = FALSE; + } else { + /* Not the last one */ + gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), + MODEST_WIZARD_DIALOG_FINISH, + FALSE); + } + + gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), + MODEST_WIZARD_DIALOG_NEXT, + enable_next); +} + +static void +on_enable_buttons (ModestWizardDialog *dialog, GtkWidget *current_page) +{ + ModestEasysetupWizardDialog *self = MODEST_EASYSETUP_WIZARD_DIALOG (dialog); + ModestEasysetupWizardDialogPrivate *priv = MODEST_EASYSETUP_WIZARD_DIALOG_GET_PRIVATE (self); + + gboolean enable_next = TRUE; + if (current_page == priv->page_welcome) { + enable_next = TRUE; + } else if (current_page == priv->page_account_details) { + /* The account details title is mandatory: */ + if (entry_is_empty(priv->entry_account_title)) + enable_next = FALSE; + } else if (current_page == priv->page_user_details) { + /* The user details username is mandatory: */ + if (entry_is_empty(priv->entry_user_username)) + enable_next = FALSE; + + /* The user details email address is mandatory: */ + if (enable_next && entry_is_empty (priv->entry_user_email)) + enable_next = FALSE; + } else if (current_page == priv->page_custom_incoming) { + /* The custom incoming server is mandatory: */ + if (entry_is_empty(priv->entry_incomingserver)) + enable_next = FALSE; + } else if (MODEST_IS_EASYSETUP_WIZARD_PAGE (current_page)) { + enable_next = !modest_easysetup_wizard_page_validate ( + MODEST_EASYSETUP_WIZARD_PAGE (current_page)); + } + + /* Enable/disable buttons */ + real_enable_buttons (dialog, enable_next); +} + +static void +modest_easysetup_wizard_dialog_class_init (ModestEasysetupWizardDialogClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + g_type_class_add_private (klass, sizeof (ModestEasysetupWizardDialogPrivate)); + + + object_class->dispose = modest_easysetup_wizard_dialog_dispose; + object_class->finalize = modest_easysetup_wizard_dialog_finalize; + + /* Provide a vfunc implementation so we can decide + * when to enable/disable the prev/next buttons. + */ + ModestWizardDialogClass *base_klass = (ModestWizardDialogClass*)(klass); + base_klass->before_next = on_before_next; + base_klass->enable_buttons = on_enable_buttons; +} + +/** + * save_to_settings: + * @self: a #ModestEasysetupWizardDialog + * + * takes information from all the wizard and stores it in settings + */ +static void +save_to_settings (ModestEasysetupWizardDialog *self) +{ + ModestEasysetupWizardDialogPrivate *priv = MODEST_EASYSETUP_WIZARD_DIALOG_GET_PRIVATE (self); + guint special_port; + gchar *provider_id; + gchar* display_name; + const gchar *username, *password; + gchar *store_hostname, *transport_hostname; + guint store_port, transport_port; + ModestProtocolRegistry *protocol_registry; + ModestProtocolType store_protocol, transport_protocol; + ModestProtocolType store_security, transport_security; + ModestProtocolType store_auth_protocol, transport_auth_protocol; + ModestServerAccountSettings *store_settings, *transport_settings; + const gchar *fullname, *email_address; + EasysetupProviderComboBox *combo; + EasysetupProviderComboBoxIdType id_type; + + priv = MODEST_EASYSETUP_WIZARD_DIALOG_GET_PRIVATE (self); + combo = EASYSETUP_PROVIDER_COMBO_BOX (priv->combo_account_serviceprovider); + protocol_registry = modest_runtime_get_protocol_registry (); + + /* Get details from the specified presets: */ + id_type = easysetup_provider_combo_box_get_active_id_type (combo); + provider_id = easysetup_provider_combo_box_get_active_provider_id (combo); + + /* Let the plugin save the settings. We do a return in order + to save an indentation level */ + if (id_type == EASYSETUP_PROVIDER_COMBO_BOX_ID_PLUGIN_PROTOCOL) { + ModestProtocol *protocol; + + protocol = modest_protocol_registry_get_protocol_by_name ( + modest_runtime_get_protocol_registry (), + MODEST_PROTOCOL_REGISTRY_PROVIDER_PROTOCOLS, + provider_id); + + if (protocol && MODEST_IS_ACCOUNT_PROTOCOL (protocol)) { + gint n_pages, i = 0; + GtkNotebook *notebook; + GList *wizard_pages = NULL; + + g_object_get (self, "wizard-notebook", ¬ebook, NULL); + n_pages = gtk_notebook_get_n_pages (notebook); + for (i = 0; i < n_pages; i++) { + GtkWidget *page = gtk_notebook_get_nth_page (notebook, i); + if (MODEST_IS_EASYSETUP_WIZARD_PAGE (page)) + wizard_pages = g_list_append (wizard_pages, page); + } + modest_account_protocol_save_wizard_settings (MODEST_ACCOUNT_PROTOCOL (protocol), + wizard_pages, + priv->settings); + g_list_free (wizard_pages); + } else { + g_warning ("The selected protocol is a plugin protocol "// + "but it's not a ModestAccountProtocol"); + } + + return; + } + + /* username and password (for both incoming and outgoing): */ + username = gtk_entry_get_text (GTK_ENTRY (priv->entry_user_username)); + password = gtk_entry_get_text (GTK_ENTRY (priv->entry_user_password)); + + store_settings = modest_account_settings_get_store_settings (priv->settings); + transport_settings = modest_account_settings_get_transport_settings (priv->settings); + + /* Incoming server: */ + /* Note: We need something as default for the transport store protocol values, + * or modest_account_mgr_add_server_account will fail. */ + store_port = 0; + store_protocol = MODEST_PROTOCOL_REGISTRY_TYPE_INVALID; + store_security = MODEST_PROTOCOLS_CONNECTION_NONE; + store_auth_protocol = MODEST_PROTOCOLS_AUTH_NONE; + + if (provider_id) { + ModestProtocolType store_provider_server_type; + gboolean store_provider_use_alternate_port; + /* Use presets: */ + store_hostname = modest_presets_get_server (priv->presets, provider_id, + TRUE /* store */); + + store_provider_server_type = modest_presets_get_info_server_type (priv->presets, + provider_id, + TRUE /* store */); + store_security = modest_presets_get_info_server_security (priv->presets, + provider_id, + TRUE /* store */); + store_auth_protocol = modest_presets_get_info_server_auth (priv->presets, + provider_id, + TRUE /* store */); + store_provider_use_alternate_port = modest_presets_get_info_server_use_alternate_port (priv->presets, + provider_id, + TRUE /* store */); + + /* We don't check for SMTP here as that is impossible for an incoming server. */ + if (store_provider_server_type == MODEST_PROTOCOL_REGISTRY_TYPE_INVALID) + store_protocol = MODEST_PROTOCOLS_STORE_POP; + else + store_protocol = store_provider_server_type; + + /* we check if there is a *special* port */ + special_port = modest_presets_get_port (priv->presets, provider_id, TRUE /* incoming */); + if (special_port != 0) + store_port = special_port; + else + store_port = get_port_from_protocol(store_provider_server_type, store_security); + + modest_server_account_settings_set_security_protocol (store_settings, + store_security); + modest_server_account_settings_set_auth_protocol (store_settings, + store_auth_protocol); + if (store_port != 0) + modest_server_account_settings_set_port (store_settings, store_port); + } else { + /* Use custom pages because no preset was specified: */ + store_hostname = g_strdup (gtk_entry_get_text (GTK_ENTRY (priv->entry_incomingserver) )); + store_protocol = easysetup_servertype_combo_box_get_active_servertype ( + EASYSETUP_SERVERTYPE_COMBO_BOX (priv->combo_incoming_servertype)); + + modest_security_options_view_save_settings ( + MODEST_SECURITY_OPTIONS_VIEW (priv->incoming_security), + priv->settings); + } + + /* now we store the common store account settings */ + modest_server_account_settings_set_hostname (store_settings, store_hostname); + modest_server_account_settings_set_username (store_settings, username); + modest_server_account_settings_set_password (store_settings, password); + modest_server_account_settings_set_protocol (store_settings, store_protocol); + + g_object_unref (store_settings); + g_free (store_hostname); + + /* Outgoing server: */ + transport_hostname = NULL; + transport_protocol = MODEST_PROTOCOL_REGISTRY_TYPE_INVALID; + transport_security = MODEST_PROTOCOLS_CONNECTION_NONE; + transport_auth_protocol = MODEST_PROTOCOLS_AUTH_NONE; + transport_port = 0; + + if (provider_id) { + ModestProtocolType transport_provider_server_type; + ModestProtocolType transport_provider_security; + + /* Use presets */ + transport_hostname = modest_presets_get_server (priv->presets, provider_id, + FALSE /* transport */); + + transport_provider_server_type = modest_presets_get_info_server_type (priv->presets, + provider_id, + FALSE /* transport */); + transport_provider_security = modest_presets_get_info_server_security (priv->presets, + provider_id, + FALSE /* transport */); + + /* Note: We need something as default, or modest_account_mgr_add_server_account will fail. */ + transport_protocol = transport_provider_server_type; + transport_security = transport_provider_security; + if (transport_security == MODEST_PROTOCOLS_CONNECTION_SSL) { + /* printf("DEBUG: %s: using secure SMTP\n", __FUNCTION__); */ + /* we check if there is a *special* port */ + special_port = modest_presets_get_port (priv->presets, provider_id, + FALSE /* transport */); + if (special_port != 0) + transport_port = special_port; + else + transport_port = 465; + transport_auth_protocol = MODEST_PROTOCOLS_AUTH_PASSWORD; + } else { + /* printf("DEBUG: %s: using non-secure SMTP\n", __FUNCTION__); */ + transport_auth_protocol = MODEST_PROTOCOLS_AUTH_NONE; + } + + modest_server_account_settings_set_security_protocol (transport_settings, + transport_security); + modest_server_account_settings_set_auth_protocol (transport_settings, + transport_auth_protocol); + if (transport_port != 0) + modest_server_account_settings_set_port (transport_settings, + transport_port); + } else { + ModestProtocolRegistry *registry; + ModestProtocol *store_proto; + + registry = modest_runtime_get_protocol_registry (); + /* Use custom pages because no preset was specified: */ + transport_hostname = g_strdup (gtk_entry_get_text (GTK_ENTRY (priv->entry_outgoingserver) )); + + store_proto = modest_protocol_registry_get_protocol_by_type (registry, + store_protocol); + + if (transport_protocol == MODEST_PROTOCOL_REGISTRY_TYPE_INVALID) { + /* fallback to SMTP if none was specified */ + g_warning ("No transport protocol was specified for store %d (%s)", + modest_protocol_get_type_id (store_proto), + modest_protocol_get_display_name (store_proto)); + transport_protocol = MODEST_PROTOCOLS_TRANSPORT_SMTP; + } + + modest_security_options_view_save_settings ( + MODEST_SECURITY_OPTIONS_VIEW (priv->outgoing_security), + priv->settings); + } + + /* now we store the common transport account settings */ + modest_server_account_settings_set_hostname (transport_settings, transport_hostname); + modest_server_account_settings_set_username (transport_settings, username); + modest_server_account_settings_set_password (transport_settings, password); + modest_server_account_settings_set_protocol (transport_settings, transport_protocol); + + g_object_unref (transport_settings); + g_free (transport_hostname); + + fullname = gtk_entry_get_text (GTK_ENTRY (priv->entry_user_name)); + email_address = gtk_entry_get_text (GTK_ENTRY (priv->entry_user_email)); + modest_account_settings_set_fullname (priv->settings, fullname); + modest_account_settings_set_email_address (priv->settings, email_address); + /* we don't set retrieve type to preserve advanced settings if + any. By default account settings are set to headers only */ + + /* Save the connection-specific SMTP server accounts. */ + modest_account_settings_set_use_connection_specific_smtp + (priv->settings, + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->checkbox_outgoing_smtp_specific))); + + display_name = get_entered_account_title (self); + modest_account_settings_set_display_name (priv->settings, display_name); + g_free (display_name); +} + diff --git a/src/hildon2/modest-easysetup-wizard-dialog.h b/src/hildon2/modest-easysetup-wizard-dialog.h new file mode 100644 index 0000000..ca075dc --- /dev/null +++ b/src/hildon2/modest-easysetup-wizard-dialog.h @@ -0,0 +1,93 @@ +/* Copyright (c) 2006, Nokia Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Nokia Corporation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _MODEST_EAYSETUP_WIZARD_DIALOG +#define _MODEST_EAYSETUP_WIZARD_DIALOG + +/* #include */ +#include "widgets/modest-wizard-dialog.h" /* We use a copied-and-improved HildonWizardDialog. */ +#include "modest-account-mgr.h" +#include + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifdef MODEST_HAVE_HILDON0_WIDGETS +#include +#else +#include +#endif /*MODEST_HAVE_HILDON0_WIDGETS*/ + +G_BEGIN_DECLS + +#define MODEST_TYPE_EASYSETUP_WIZARD_DIALOG modest_easysetup_wizard_dialog_get_type() + +#define MODEST_EASYSETUP_WIZARD_DIALOG(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + MODEST_TYPE_EASYSETUP_WIZARD_DIALOG, ModestEasysetupWizardDialog)) + +#define MODEST_EASYSETUP_WIZARD_DIALOG_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), \ + MODEST_TYPE_EASYSETUP_WIZARD_DIALOG, ModestEasysetupWizardDialogClass)) + +#define MODEST_IS_EASYSETUP_WIZARD_DIALOG(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + MODEST_TYPE_EASYSETUP_WIZARD_DIALOG)) + +#define MODEST_EASYSETUP_IS_WIZARD_DIALOG_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + MODEST_TYPE_EASYSETUP_WIZARD_DIALOG)) + +#define MODEST_EASYSETUP_WIZARD_DIALOG_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + MODEST_TYPE_EASYSETUP_WIZARD_DIALOG, ModestEasysetupWizardDialogClass)) + +typedef struct { + ModestWizardDialog parent; + +} ModestEasysetupWizardDialog; + +typedef struct { + ModestWizardDialogClass parent_class; + +} ModestEasysetupWizardDialogClass; + +GType modest_easysetup_wizard_dialog_get_type (void); + +/* + * NOTE: can be instantiated only once; after that, this function will + * return NULL, until the one before is destroyed; if it returns NULL + * it will gtk_window_present the existing one. + */ +ModestEasysetupWizardDialog* modest_easysetup_wizard_dialog_new (void); + +G_END_DECLS + +#endif /* _MODEST_EAYSETUP_WIZARD_DIALOG */ diff --git a/src/hildon2/modest-hildon-includes.h b/src/hildon2/modest-hildon-includes.h new file mode 100644 index 0000000..6dd1306 --- /dev/null +++ b/src/hildon2/modest-hildon-includes.h @@ -0,0 +1,156 @@ +/* Copyright (c) 2006, Nokia Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Nokia Corporation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +k * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __MODEST_HILDON_INCLUDES__ +#define __MODEST_HILDON_INCLUDES__ + +#ifdef HAVE_CONFIG_H +#include +#endif + +/* helplib to use */ +#ifdef MODEST_HAVE_OSSO_HELP +#include +#else +#ifdef MODEST_HAVE_HILDON_HELP +#include +#endif /*MODEST_HAVE_HILDON_HELP*/ +#endif /*MODEST_HAVE_OSSO_HELP*/ + +/* mimelib to use */ +#ifdef MODEST_HAVE_OSSO_MIME +#include +#include +#else +#ifdef MODEST_HAVE_HILDON_MIME +#include +#include +#endif /*MODEST_HAVE_HILDON_MIME*/ +#endif /*MODEST_HAVE_OSSO_MIME*/ + + +#ifdef MODEST_HAVE_HILDON_NOTIFY +#include +#endif /*MODEST_HILDON_NOTIFY*/ + +#if MODEST_HILDON_API == 0 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#else + +#if MODEST_HILDON_API >= 1 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif /*__MODEST_HAVE_HILDON1_WIDGETS*/ +#endif /*__MODEST_HAVE_HILDON0_WIDGETS_*/ + + +/* backward compatibility... */ +#ifdef MODEST_HAVE_OSSO_MIME +#define hildon_mime_open_file_with_mime_type osso_mime_open_file_with_mime_type +#define hildon_mime_open_file osso_mime_open_file + +#define HildonURIAction OssoURIAction +#define hildon_uri_get_scheme_from_uri osso_uri_get_scheme_from_uri +#define hildon_uri_get_actions osso_uri_get_actions +#define hildon_uri_get_actions_by_uri osso_uri_get_actions_by_uri +#define hildon_uri_action_get_translation osso_uri_action_get_translation +#define hildon_uri_is_default_action osso_uri_is_default_action +#define hildon_uri_free_actions osso_uri_free_actions + +/* service->name */ +#define hildon_uri_action_get_service osso_uri_action_get_name +#define hildon_uri_open osso_uri_open + +#define hildon_mime_get_icon_names osso_mime_get_icon_names +#endif /*MODEST_HAVE_OSSO_MIME*/ + +/* helplib to use */ +#ifdef MODEST_HAVE_OSSO_HELP +#define hildon_help_show ossohelp_show +#define hildon_help_dialog_help_enable ossohelp_dialog_help_enable +#define HILDON_HELP_SHOW_DIALOG OSSO_HELP_SHOW_DIALOG +#else +#ifdef MODEST_HAVE_HILDON_HELP +/* nothing */ +#endif /*MODEST_HAVE_HILDON_HELP*/ +#endif /*MODEST_HAVE_OSSO_HELP*/ + +/* some extra #defines, so it will compile with the 'normal' gtk */ +#ifndef MODEST_HAVE_HILDON_GTK +#define hildon_gtk_entry_set_input_mode(a,b) \ + g_warning ("%s: hildon_gtk_entry_set_input_mode requires gtk-hildon", __FUNCTION__) +#define hildon_gtk_text_view_set_input_mode(a,b) \ + g_warning ("%s: hildon_gtk_text_view_set_input_mode requires gtk-hildon", __FUNCTION__) +#define gtk_widget_tap_and_hold_setup(a,b,c,d) \ + g_warning ("%s: gtk_widget_tap_and_hold_setup requires gtk-hildon", __FUNCTION__) + +typedef enum +{ + GTK_INVALID_INPUT_MAX_CHARS_REACHED, + GTK_INVALID_INPUT_MODE_RESTRICTION +} GtkInvalidInputType; + +typedef enum +{ + HILDON_GTK_INPUT_MODE_ALPHA = 1 << 0, + HILDON_GTK_INPUT_MODE_NUMERIC = 1 << 1, + HILDON_GTK_INPUT_MODE_SPECIAL = 1 << 2, + HILDON_GTK_INPUT_MODE_HEXA = 1 << 3, + HILDON_GTK_INPUT_MODE_TELE = 1 << 4, + HILDON_GTK_INPUT_MODE_FULL = (HILDON_GTK_INPUT_MODE_ALPHA | HILDON_GTK_INPUT_MODE_NUMERIC | HILDON_GTK_INPUT_MODE_SPECIAL), + HILDON_GTK_INPUT_MODE_MULTILINE = 1 << 28, + HILDON_GTK_INPUT_MODE_INVISIBLE = 1 << 29, + HILDON_GTK_INPUT_MODE_AUTOCAP = 1 << 30, + HILDON_GTK_INPUT_MODE_DICTIONARY = 1 << 31 +} HildonGtkInputMode; +#endif /* !MODEST_HAVE_HILDON_GTK */ + +#endif /*__MODEST_HILDON_INCLUDES__*/ diff --git a/src/hildon2/modest-hildon-sort-dialog.c b/src/hildon2/modest-hildon-sort-dialog.c new file mode 100644 index 0000000..b2e56da --- /dev/null +++ b/src/hildon2/modest-hildon-sort-dialog.c @@ -0,0 +1,133 @@ +/* Copyright (c) 2006, Nokia Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Nokia Corporation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "modest-hildon-sort-dialog.h" +#include "widgets/modest-sort-criterium-view.h" + + +static gint modest_hildon_sort_dialog_add_sort_key (ModestSortCriteriumView *self, + const gchar *sort_key); +static void modest_hildon_sort_dialog_set_sort_key (ModestSortCriteriumView *self, gint key); +static void modest_hildon_sort_dialog_set_sort_order (ModestSortCriteriumView *self, GtkSortType sort_type); +static gint modest_hildon_sort_dialog_get_sort_key (ModestSortCriteriumView *self); +static GtkSortType modest_hildon_sort_dialog_get_sort_order (ModestSortCriteriumView *self); +static void modest_sort_criterium_view_init (gpointer g_iface, gpointer iface_data); + +G_DEFINE_TYPE_EXTENDED (ModestHildonSortDialog, + modest_hildon_sort_dialog, + HILDON_TYPE_SORT_DIALOG, + 0, + G_IMPLEMENT_INTERFACE (MODEST_TYPE_SORT_CRITERIUM_VIEW, modest_sort_criterium_view_init)); + +static void +modest_hildon_sort_dialog_finalize (GObject *object) +{ + G_OBJECT_CLASS (modest_hildon_sort_dialog_parent_class)->finalize (object); +} + +static void +modest_hildon_sort_dialog_class_init (ModestHildonSortDialogClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = modest_hildon_sort_dialog_finalize; + +} + +static void +modest_hildon_sort_dialog_init (ModestHildonSortDialog *self) +{ +} + +static gint +modest_hildon_sort_dialog_add_sort_key (ModestSortCriteriumView *self, + const gchar *sort_key) +{ + g_return_val_if_fail (MODEST_IS_HILDON_SORT_DIALOG (self), 0); + + return hildon_sort_dialog_add_sort_key (HILDON_SORT_DIALOG (self), sort_key); +} + +static void +modest_hildon_sort_dialog_set_sort_key (ModestSortCriteriumView *self, gint key) +{ + g_return_if_fail (MODEST_IS_HILDON_SORT_DIALOG (self)); + + hildon_sort_dialog_set_sort_key (HILDON_SORT_DIALOG (self), key); +} + +static void +modest_hildon_sort_dialog_set_sort_order (ModestSortCriteriumView *self, GtkSortType sort_type) +{ + g_return_if_fail (MODEST_IS_HILDON_SORT_DIALOG (self)); + + hildon_sort_dialog_set_sort_order (HILDON_SORT_DIALOG (self), sort_type); +} + +static gint +modest_hildon_sort_dialog_get_sort_key (ModestSortCriteriumView *self) +{ + g_return_val_if_fail (MODEST_IS_HILDON_SORT_DIALOG (self), 0); + + return hildon_sort_dialog_get_sort_key (HILDON_SORT_DIALOG (self)); +} + +static GtkSortType +modest_hildon_sort_dialog_get_sort_order (ModestSortCriteriumView *self) +{ + g_return_val_if_fail (MODEST_IS_HILDON_SORT_DIALOG (self), GTK_SORT_ASCENDING); + + return hildon_sort_dialog_get_sort_order (HILDON_SORT_DIALOG (self)); +} + +static void +modest_sort_criterium_view_init (gpointer g_iface, + gpointer iface_data) +{ + ModestSortCriteriumViewIface *iface = (ModestSortCriteriumViewIface *) g_iface; + + iface->add_sort_key_func = modest_hildon_sort_dialog_add_sort_key; + iface->get_sort_key_func = modest_hildon_sort_dialog_get_sort_key; + iface->set_sort_key_func = modest_hildon_sort_dialog_set_sort_key; + iface->get_sort_order_func = modest_hildon_sort_dialog_get_sort_order; + iface->set_sort_order_func = modest_hildon_sort_dialog_set_sort_order; +} + +GtkWidget* +modest_hildon_sort_dialog_new (GtkWindow *parent) +{ + GtkWidget *result = g_object_new (MODEST_TYPE_HILDON_SORT_DIALOG, NULL); + + + if (parent) + gtk_window_set_transient_for(GTK_WINDOW(result), parent); + + return result; +} + diff --git a/src/hildon2/modest-hildon-sort-dialog.h b/src/hildon2/modest-hildon-sort-dialog.h new file mode 100644 index 0000000..8fd7227 --- /dev/null +++ b/src/hildon2/modest-hildon-sort-dialog.h @@ -0,0 +1,77 @@ +/* Copyright (c) 2006, Nokia Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Nokia Corporation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __MODEST_HILDON_SORT_DIALOG +#define __MODEST_HILDON_SORT_DIALOG + +#include +#include +#include + +G_BEGIN_DECLS + +#define MODEST_TYPE_HILDON_SORT_DIALOG modest_hildon_sort_dialog_get_type() + +#define MODEST_HILDON_SORT_DIALOG(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + MODEST_TYPE_HILDON_SORT_DIALOG, ModestHildonSortDialog)) + +#define MODEST_HILDON_SORT_DIALOG_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), \ + MODEST_TYPE_HILDON_SORT_DIALOG, ModestHildonSortDialogClass)) + +#define MODEST_IS_HILDON_SORT_DIALOG(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + MODEST_TYPE_HILDON_SORT_DIALOG)) + +#define MODEST_IS_HILDON_SORT_DIALOG_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + MODEST_TYPE_HILDON_SORT_DIALOG)) + +#define MODEST_HILDON_SORT_DIALOG_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + MODEST_TYPE_HILDON_SORT_DIALOG, ModestHildonSortDialogClass)) + +typedef struct { + HildonSortDialog parent; + +} ModestHildonSortDialog; + +typedef struct { + HildonSortDialogClass parent_class; + +} ModestHildonSortDialogClass; + +GType modest_hildon_sort_dialog_get_type (void); + +GtkWidget* modest_hildon_sort_dialog_new (GtkWindow *parent); + +G_END_DECLS + +#endif /* __MODEST_HILDON_SORT_DIALOG */ diff --git a/src/hildon2/modest-icon-names.h b/src/hildon2/modest-icon-names.h new file mode 100644 index 0000000..5ab97e1 --- /dev/null +++ b/src/hildon2/modest-icon-names.h @@ -0,0 +1,113 @@ +/* Copyright (c) 2006, Nokia Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Nokia Corporation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +/* modest-tny-icon-names.h */ + + +#ifndef __MODEST_TNY_ICON_NAMES_H__ +#define __MODEST_TNY_ICON_NAMES_H__ + +/* icons */ + +#define MODEST_APP_ICON "qgn_list_messagin" +#define MODEST_APP_MSG_VIEW_ICON "qgn_list_messagin_viewer" +#define MODEST_APP_MSG_EDIT_ICON "qgn_list_messagin_editor" + +#define MODEST_HEADER_ICON_READ "qgn_list_messagin_mail" +#define MODEST_HEADER_ICON_UNREAD "" +#define MODEST_HEADER_ICON_DELETED "qgn_list_messagin_mail_deleted" +#define MODEST_HEADER_ICON_ATTACH "qgn_list_gene_attacpap" +#define MODEST_HEADER_ICON_HIGH "qgn_list_messaging_high" +#define MODEST_HEADER_ICON_LOW "qgn_list_messaging_low" + +/* + * until we have the custom cell renderer, we use the hacked icons below; + * don't remove!!! + */ +#define MODEST_HEADER_ICON_ATTACH_HIGH_PRIORITY PIXMAP_PREFIX"modest_high_attachment.png" +#define MODEST_HEADER_ICON_ATTACH_LOW_PRIORITY PIXMAP_PREFIX"modest_low_attachment.png" +#define MODEST_HEADER_ICON_ATTACH_NORM_PRIORITY PIXMAP_PREFIX"modest_normal_attachment.png" +#define MODEST_HEADER_ICON_HIGH_PRIORITY PIXMAP_PREFIX"modest_high_no_attachment.png" +#define MODEST_HEADER_ICON_LOW_PRIORITY PIXMAP_PREFIX"modest_low_no_attachment.png" +#define MODEST_HEADER_ICON_NORM_PRIORITY PIXMAP_PREFIX"modest_normal_no_attachment.png" +/* + * + */ + +#define MODEST_FOLDER_ICON_OPEN "qgn_list_gene_fldr_opn" +#define MODEST_FOLDER_ICON_CLOSED "qgn_list_gene_fldr_cls" + +#define MODEST_FOLDER_ICON_ACCOUNT "qgn_list_browser" +#define MODEST_FOLDER_ICON_INBOX "qgn_list_messagin_inbox" +#define MODEST_FOLDER_ICON_OUTBOX "qgn_list_messagin_outbox" +#define MODEST_FOLDER_ICON_SENT "qgn_list_messagin_sent" +#define MODEST_FOLDER_ICON_TRASH "qgn_toolb_gene_deletebutton" +#define MODEST_FOLDER_ICON_JUNK "qgn_toolb_gene_deletebutton" +#define MODEST_FOLDER_ICON_DRAFTS "qgn_list_messagin_drafts" +#define MODEST_FOLDER_ICON_NORMAL "qgn_list_gene_fldr_cls" +#define MODEST_FOLDER_ICON_LOCAL_FOLDERS "qgn_list_shell_mydevice" +#define MODEST_FOLDER_ICON_MMC "qgn_list_gene_mmc" + + +/* toolbar */ +#define MODEST_TOOLBAR_ICON_MAIL_SEND "qgn_toolb_messagin_send" +#define MODEST_TOOLBAR_ICON_NEW_MAIL "qgn_list_messagin_editor" +#define MODEST_TOOLBAR_ICON_SEND_RECEIVE "qgn_toolb_messagin_sendreceive" +#define MODEST_TOOLBAR_ICON_REPLY "qgn_toolb_messagin_reply" +#define MODEST_TOOLBAR_ICON_REPLY_ALL "qgn_toolb_messagin_replytoall" +#define MODEST_TOOLBAR_ICON_FORWARD "qgn_toolb_messagin_forward" +#define MODEST_TOOLBAR_ICON_DELETE "qgn_toolb_gene_deletebutton" +#define MODEST_TOOLBAR_ICON_FORMAT_BULLETS "qgn_list_gene_bullets" +#define MODEST_TOOLBAR_ICON_SPLIT_VIEW "qgn_toolb_rss_fldonoff" +#define MODEST_TOOLBAR_ICON_SORT "qgn_list_sort" +#define MODEST_TOOLBAR_ICON_REFRESH "qgn_toolb_gene_refresh" +#define MODEST_TOOLBAR_ICON_MOVE_TO_FOLDER "qgn_toolb_gene_movetofldr" +#define MODEST_TOOLBAR_ICON_BOLD "qgn_list_gene_bold" +#define MODEST_TOOLBAR_ICON_ITALIC "qgn_list_gene_italic" +#define MODEST_TOOLBAR_ICON_NEXT "qgn_toolb_gene_forward" +#define MODEST_TOOLBAR_ICON_PREV "qgn_toolb_gene_back" +#define MODEST_TOOLBAR_ICON_FIND "qgn_toolb_gene_find" + +/* Stock icon names */ +#define MODEST_STOCK_MAIL_SEND "modest-stock-mail-send" +#define MODEST_STOCK_NEW_MAIL "modest-stock-new-mail" +#define MODEST_STOCK_SEND_RECEIVE "modest-stock-send-receive" +#define MODEST_STOCK_REPLY "modest-stock-reply" +#define MODEST_STOCK_REPLY_ALL "modest-stock-reply-all" +#define MODEST_STOCK_FORWARD "modest-stock-forward" +#define MODEST_STOCK_DELETE "modest-stock-delete" +#define MODEST_STOCK_NEXT "modest-stock-next" +#define MODEST_STOCK_PREV "modest-stock-prev" +#define MODEST_STOCK_STOP "modest-stock-stop" +#define MODEST_STOCK_SPLIT_VIEW "modest-stock-split-view" +#define MODEST_STOCK_SORT "modest-stock-sort" +#define MODEST_STOCK_REFRESH "modest-stock-refresh" + +#endif /*__MODEST_TNY_ICON_NAMES_H__*/ diff --git a/src/hildon2/modest-maemo-global-settings-dialog.c b/src/hildon2/modest-maemo-global-settings-dialog.c new file mode 100644 index 0000000..3d192ea --- /dev/null +++ b/src/hildon2/modest-maemo-global-settings-dialog.c @@ -0,0 +1,523 @@ +/* Copyright (c) 2006, Nokia Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Nokia Corporation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include "modest-runtime.h" +#include "widgets/modest-global-settings-dialog-priv.h" +#include "widgets/modest-combo-box.h" +#include "maemo/modest-maemo-global-settings-dialog.h" +#include "widgets/modest-ui-constants.h" +#include + + +#define MSG_SIZE_MAX_VAL 5000 +#define MSG_SIZE_DEF_VAL 1000 +#define MSG_SIZE_MIN_VAL 1 + +#define DEFAULT_FOCUS_WIDGET "default-focus-widget" + +/* 'private'/'protected' functions */ +static void modest_maemo_global_settings_dialog_class_init (ModestMaemoGlobalSettingsDialogClass *klass); +static void modest_maemo_global_settings_dialog_init (ModestMaemoGlobalSettingsDialog *obj); +static void modest_maemo_global_settings_dialog_finalize (GObject *obj); + +static ModestConnectedVia current_connection (void); + +/* list my signals */ +enum { + /* MY_SIGNAL_1, */ + /* MY_SIGNAL_2, */ + LAST_SIGNAL +}; + +static GtkWidget* create_updating_page (ModestMaemoGlobalSettingsDialog *self); +static GtkWidget* create_composing_page (ModestMaemoGlobalSettingsDialog *self); + +static gboolean on_range_error (HildonNumberEditor *editor, + HildonNumberEditorErrorType type, + gpointer user_data); + +static void on_size_notify (HildonNumberEditor *editor, + GParamSpec *arg1, + gpointer user_data); + +static void on_auto_update_toggled (GtkToggleButton *togglebutton, + gpointer user_data); + +static gboolean on_inner_tabs_key_pressed (GtkWidget *widget, + GdkEventKey *event, + gpointer user_data); + +typedef struct _ModestMaemoGlobalSettingsDialogPrivate ModestMaemoGlobalSettingsDialogPrivate; +struct _ModestMaemoGlobalSettingsDialogPrivate { + ModestPairList *connect_via_list; + gint switch_handler; +}; +#define MODEST_MAEMO_GLOBAL_SETTINGS_DIALOG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \ + MODEST_TYPE_MAEMO_GLOBAL_SETTINGS_DIALOG, \ + ModestMaemoGlobalSettingsDialogPrivate)) +/* globals */ +static GtkDialogClass *parent_class = NULL; + +/* uncomment the following if you have defined any signals */ +/* static guint signals[LAST_SIGNAL] = {0}; */ + +GType +modest_maemo_global_settings_dialog_get_type (void) +{ + static GType my_type = 0; + if (!my_type) { + static const GTypeInfo my_info = { + sizeof(ModestMaemoGlobalSettingsDialogClass), + NULL, /* base init */ + NULL, /* base finalize */ + (GClassInitFunc) modest_maemo_global_settings_dialog_class_init, + NULL, /* class finalize */ + NULL, /* class data */ + sizeof(ModestMaemoGlobalSettingsDialog), + 1, /* n_preallocs */ + (GInstanceInitFunc) modest_maemo_global_settings_dialog_init, + NULL + }; + my_type = g_type_register_static (MODEST_TYPE_GLOBAL_SETTINGS_DIALOG, + "ModestMaemoGlobalSettingsDialog", + &my_info, 0); + } + return my_type; +} + +static void +modest_maemo_global_settings_dialog_class_init (ModestMaemoGlobalSettingsDialogClass *klass) +{ + GObjectClass *gobject_class; + gobject_class = (GObjectClass*) klass; + + parent_class = g_type_class_peek_parent (klass); + gobject_class->finalize = modest_maemo_global_settings_dialog_finalize; + + g_type_class_add_private (gobject_class, sizeof(ModestMaemoGlobalSettingsDialogPrivate)); + + MODEST_GLOBAL_SETTINGS_DIALOG_CLASS (klass)->current_connection_func = current_connection; +} + +typedef struct { + ModestMaemoGlobalSettingsDialog *dia; + GtkWidget *focus_widget; +} SwitchPageHelper; + +static gboolean +idle_select_default_focus (gpointer data) +{ + ModestGlobalSettingsDialogPrivate *ppriv; + ModestMaemoGlobalSettingsDialogPrivate *priv; + SwitchPageHelper *helper; + + helper = (SwitchPageHelper *) data; + priv = MODEST_MAEMO_GLOBAL_SETTINGS_DIALOG_GET_PRIVATE (helper->dia); + ppriv = MODEST_GLOBAL_SETTINGS_DIALOG_GET_PRIVATE (helper->dia); + + /* Grab focus, we need to block in order to prevent a + recursive call to this callback */ + g_signal_handler_block (G_OBJECT (ppriv->notebook), priv->switch_handler); + + /* This is a GDK lock because we are an idle callback and + * the code below is or does Gtk+ code */ + + gdk_threads_enter (); /* CHECKED */ + gtk_widget_grab_focus (helper->focus_widget); + gdk_threads_leave (); /* CHECKED */ + + g_signal_handler_unblock (G_OBJECT (ppriv->notebook), priv->switch_handler); + g_free (helper); + + return FALSE; +} + + +static void +on_switch_page (GtkNotebook *notebook, GtkNotebookPage *page, guint page_num, gpointer user_data) +{ + /* grab the focus to the default element in the current page */ + GtkWidget *selected_page = NULL, *focus_item = NULL; + SwitchPageHelper *helper; + + selected_page = gtk_notebook_get_nth_page (notebook, page_num); + focus_item = GTK_WIDGET(g_object_get_data (G_OBJECT(selected_page), DEFAULT_FOCUS_WIDGET)); + if (!focus_item) { + g_printerr ("modest: cannot get focus item\n"); + return; + } + + /* Create the helper */ + helper = g_malloc0 (sizeof (SwitchPageHelper)); + helper->dia = MODEST_MAEMO_GLOBAL_SETTINGS_DIALOG (user_data); + helper->focus_widget = focus_item; + + /* Focus the widget in an idle. We need to do this in an idle, + because this handler is executed *before* the page was + really switched, so the focus is not placed in the right + widget */ + g_idle_add (idle_select_default_focus, helper); +} + + +static void +modest_maemo_global_settings_dialog_init (ModestMaemoGlobalSettingsDialog *self) +{ + ModestMaemoGlobalSettingsDialogPrivate *priv; + ModestGlobalSettingsDialogPrivate *ppriv; + + priv = MODEST_MAEMO_GLOBAL_SETTINGS_DIALOG_GET_PRIVATE (self); + ppriv = MODEST_GLOBAL_SETTINGS_DIALOG_GET_PRIVATE (self); + + ppriv->updating_page = create_updating_page (self); + ppriv->composing_page = create_composing_page (self); + + /* Add the buttons: */ + gtk_dialog_add_button (GTK_DIALOG (self), _("mcen_bd_dialog_ok"), GTK_RESPONSE_OK); + gtk_dialog_add_button (GTK_DIALOG (self), _("mcen_bd_dialog_cancel"), GTK_RESPONSE_CANCEL); + + /* Set the default focusable widgets */ + g_object_set_data (G_OBJECT(ppriv->updating_page), DEFAULT_FOCUS_WIDGET, + (gpointer)ppriv->auto_update); + g_object_set_data (G_OBJECT(ppriv->composing_page), DEFAULT_FOCUS_WIDGET, + (gpointer)ppriv->msg_format); + + /* Add the notebook pages: */ + gtk_notebook_append_page (GTK_NOTEBOOK (ppriv->notebook), ppriv->updating_page, + gtk_label_new (_("mcen_ti_options_updating"))); + gtk_notebook_append_page (GTK_NOTEBOOK (ppriv->notebook), ppriv->composing_page, + gtk_label_new (_("mcen_ti_options_composing"))); + + gtk_container_add (GTK_CONTAINER (GTK_DIALOG (self)->vbox), ppriv->notebook); + gtk_container_set_border_width (GTK_CONTAINER (GTK_DIALOG (self)->vbox), MODEST_MARGIN_HALF); + + gtk_window_set_default_size (GTK_WINDOW (self), 700, 300); + + g_signal_connect (G_OBJECT (self), "key-press-event", + G_CALLBACK (on_inner_tabs_key_pressed), self); + priv->switch_handler = g_signal_connect (G_OBJECT(ppriv->notebook), "switch-page", + G_CALLBACK(on_switch_page), self); + + /* Load current config */ + _modest_global_settings_dialog_load_conf (MODEST_GLOBAL_SETTINGS_DIALOG (self)); + + /* Set first page */ + gtk_notebook_set_current_page (GTK_NOTEBOOK (ppriv->notebook), 0); + + hildon_help_dialog_help_enable (GTK_DIALOG(self), "applications_email_options_dialog", + modest_maemo_utils_get_osso_context()); +} + +static void +modest_maemo_global_settings_dialog_finalize (GObject *obj) +{ + ModestGlobalSettingsDialogPrivate *ppriv; + ModestMaemoGlobalSettingsDialogPrivate *priv; + + priv = MODEST_MAEMO_GLOBAL_SETTINGS_DIALOG_GET_PRIVATE (obj); + ppriv = MODEST_GLOBAL_SETTINGS_DIALOG_GET_PRIVATE (obj); + + if (priv->switch_handler && ppriv->notebook) { + /* TODO: This causes a g_warning and a valgrind mem error: */ + /* g_signal_handler_disconnect (G_OBJECT (ppriv->notebook), priv->switch_handler);*/ + priv->switch_handler = 0; + } + +/* free/unref instance resources here */ + G_OBJECT_CLASS(parent_class)->finalize (obj); +} + +GtkWidget* +modest_maemo_global_settings_dialog_new (void) +{ + return GTK_WIDGET(g_object_new(MODEST_TYPE_MAEMO_GLOBAL_SETTINGS_DIALOG, NULL)); +} + +/* + * Creates the updating page + */ +static GtkWidget* +create_updating_page (ModestMaemoGlobalSettingsDialog *self) +{ + GtkWidget *vbox, *vbox_update, *vbox_limit, *caption; + GtkSizeGroup *size_group; + ModestGlobalSettingsDialogPrivate *ppriv; + GtkWidget *scrollwin = NULL; + GtkAdjustment *focus_adjustment = NULL; + + ppriv = MODEST_GLOBAL_SETTINGS_DIALOG_GET_PRIVATE (self); + scrollwin = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrollwin), + GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); + + vbox = gtk_vbox_new (FALSE, MODEST_MARGIN_DEFAULT); + + vbox_update = gtk_vbox_new (FALSE, MODEST_MARGIN_DEFAULT); + size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); + + /* Auto update */ + ppriv->auto_update = gtk_check_button_new (); + caption = hildon_caption_new (size_group, + _("mcen_fi_options_autoupdate"), + ppriv->auto_update, + NULL, + HILDON_CAPTION_MANDATORY); + gtk_box_pack_start (GTK_BOX (vbox_update), caption, FALSE, FALSE, MODEST_MARGIN_HALF); + g_signal_connect (ppriv->auto_update, "toggled", G_CALLBACK (on_auto_update_toggled), self); + + /* Connected via */ + + /* Note: This ModestPairList* must exist for as long as the combo + * that uses it, because the ModestComboBox uses the ID opaquely, + * so it can't know how to manage its memory. */ + ppriv->connect_via_list = _modest_global_settings_dialog_get_connected_via (); + ppriv->connect_via = modest_combo_box_new (ppriv->connect_via_list, g_int_equal); + + caption = hildon_caption_new (size_group, + _("mcen_fi_options_connectiontype"), + ppriv->connect_via, + NULL, + HILDON_CAPTION_MANDATORY); + gtk_box_pack_start (GTK_BOX (vbox_update), caption, FALSE, FALSE, MODEST_MARGIN_HALF); + + /* Update interval */ + + /* Note: This ModestPairList* must exist for as long as the combo + * that uses it, because the ModestComboBox uses the ID opaquely, + * so it can't know how to manage its memory. */ + ppriv->update_interval_list = _modest_global_settings_dialog_get_update_interval (); + ppriv->update_interval = modest_combo_box_new (ppriv->update_interval_list, g_int_equal); + + caption = hildon_caption_new (size_group, + _("mcen_fi_options_updateinterval"), + ppriv->update_interval, + NULL, + HILDON_CAPTION_MANDATORY); + gtk_box_pack_start (GTK_BOX (vbox_update), caption, FALSE, FALSE, MODEST_MARGIN_HALF); + + /* Add to vbox */ + gtk_box_pack_start (GTK_BOX (vbox), vbox_update, FALSE, FALSE, MODEST_MARGIN_HALF); + + /* Separator */ + gtk_box_pack_start (GTK_BOX (vbox), gtk_hseparator_new (), FALSE, FALSE, MODEST_MARGIN_HALF); + + /* Limits */ + vbox_limit = gtk_vbox_new (FALSE, MODEST_MARGIN_DEFAULT); + size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); + + /* Size limit */ + ppriv->size_limit = hildon_number_editor_new (MSG_SIZE_MIN_VAL, MSG_SIZE_MAX_VAL); + hildon_number_editor_set_value (HILDON_NUMBER_EDITOR (ppriv->size_limit), MSG_SIZE_DEF_VAL); + g_signal_connect (ppriv->size_limit, "range_error", G_CALLBACK (on_range_error), self); + g_signal_connect (ppriv->size_limit, "notify", G_CALLBACK (on_size_notify), self); + caption = hildon_caption_new (size_group, + _("mcen_fi_advsetup_sizelimit"), + ppriv->size_limit, + NULL, + HILDON_CAPTION_MANDATORY); + gtk_box_pack_start (GTK_BOX (vbox_limit), caption, FALSE, FALSE, MODEST_MARGIN_HALF); + + /* Play sound */ + ppriv->play_sound = gtk_check_button_new (); + caption = hildon_caption_new (size_group, + _("mcen_fi_options_playsound"), + ppriv->play_sound, + NULL, + HILDON_CAPTION_MANDATORY); + gtk_box_pack_start (GTK_BOX (vbox_limit), caption, FALSE, FALSE, MODEST_MARGIN_HALF); + + /* Add to vbox */ + gtk_box_pack_start (GTK_BOX (vbox), vbox_limit, FALSE, FALSE, MODEST_MARGIN_HALF); + gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scrollwin), vbox); + focus_adjustment = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (scrollwin)); + gtk_container_set_focus_vadjustment (GTK_CONTAINER (vbox), focus_adjustment); + gtk_widget_show (scrollwin); + + return scrollwin; +} + +/* + * Creates the composing page + */ +static GtkWidget* +create_composing_page (ModestMaemoGlobalSettingsDialog *self) +{ + GtkWidget *vbox; + GtkSizeGroup *size_group; + ModestGlobalSettingsDialogPrivate *ppriv; + GtkWidget *caption; + + ppriv = MODEST_GLOBAL_SETTINGS_DIALOG_GET_PRIVATE (self); + size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); + vbox = gtk_vbox_new (FALSE, MODEST_MARGIN_DEFAULT); + + /* Update interval */ + + /* Note: This ModestPairList* must exist for as long as the combo + * that uses it, because the ModestComboBox uses the ID opaquely, + * so it can't know how to manage its memory. */ + ppriv->msg_format_list = _modest_global_settings_dialog_get_msg_formats (); + ppriv->msg_format = modest_combo_box_new (ppriv->msg_format_list, g_int_equal); + + caption = hildon_caption_new (size_group, + _("mcen_fi_options_messageformat"), + ppriv->msg_format, + NULL, + HILDON_CAPTION_MANDATORY); + gtk_box_pack_start (GTK_BOX (vbox), caption, FALSE, FALSE, MODEST_MARGIN_HALF); + + return vbox; +} + +static void +on_auto_update_toggled (GtkToggleButton *togglebutton, + gpointer user_data) +{ + ModestGlobalSettingsDialogPrivate *ppriv; + GtkWidget *caption1, *caption2; + + ppriv = MODEST_GLOBAL_SETTINGS_DIALOG_GET_PRIVATE (user_data); + caption1 = gtk_widget_get_ancestor (ppriv->connect_via, HILDON_TYPE_CAPTION); + caption2 = gtk_widget_get_ancestor (ppriv->update_interval, HILDON_TYPE_CAPTION); + + if (gtk_toggle_button_get_active (togglebutton)) { + gtk_widget_set_sensitive (caption1, TRUE); + gtk_widget_set_sensitive (caption2, TRUE); + } else { + gtk_widget_set_sensitive (caption1, FALSE); + gtk_widget_set_sensitive (caption2, FALSE); + } +} + +static gboolean +on_range_error (HildonNumberEditor *editor, + HildonNumberEditorErrorType type, + gpointer user_data) +{ + gchar *msg; + gint new_val; + + switch (type) { +#ifdef MODEST_HAVE_HILDON0_WIDGETS + case MAXIMUM_VALUE_EXCEED: +#else + case HILDON_NUMBER_EDITOR_ERROR_MAXIMUM_VALUE_EXCEED: +#endif + msg = g_strdup_printf (dgettext("hildon-libs", "ckct_ib_maximum_value"), MSG_SIZE_MAX_VAL); + new_val = MSG_SIZE_MAX_VAL; + break; +#ifdef MODEST_HAVE_HILDON0_WIDGETS + case MINIMUM_VALUE_EXCEED: +#else + case HILDON_NUMBER_EDITOR_ERROR_MINIMUM_VALUE_EXCEED: +#endif + msg = g_strdup_printf (dgettext("hildon-libs", "ckct_ib_minimum_value"), MSG_SIZE_MIN_VAL); + new_val = MSG_SIZE_MIN_VAL; + break; +#ifdef MODEST_HAVE_HILDON0_WIDGETS + case ERRONEOUS_VALUE: +#else + case HILDON_NUMBER_EDITOR_ERROR_ERRONEOUS_VALUE: +#endif + msg = g_strdup_printf (dgettext("hildon-libs", "ckct_ib_set_a_value_within_range"), + MSG_SIZE_MIN_VAL, + MSG_SIZE_MAX_VAL); + /* FIXME: use the previous */ + new_val = MSG_SIZE_DEF_VAL; + break; + default: + g_return_val_if_reached (FALSE); + } + + /* Restore value */ + hildon_number_editor_set_value (editor, new_val); + + /* Show error */ + hildon_banner_show_information (GTK_WIDGET (user_data), NULL, msg); + + /* Free */ + g_free (msg); + + return TRUE; +} + +static void +on_size_notify (HildonNumberEditor *editor, + GParamSpec *arg1, + gpointer user_data) +{ + ModestMaemoGlobalSettingsDialog *dialog = MODEST_MAEMO_GLOBAL_SETTINGS_DIALOG (user_data); + gint value = hildon_number_editor_get_value (editor); + + gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), GTK_RESPONSE_OK, value > 0); +} + +static ModestConnectedVia +current_connection (void) +{ + return modest_platform_get_current_connection (); +} + +static gboolean +on_inner_tabs_key_pressed (GtkWidget *widget, + GdkEventKey *event, + gpointer user_data) +{ + ModestGlobalSettingsDialogPrivate *ppriv; + gboolean retval = FALSE; + + ppriv = MODEST_GLOBAL_SETTINGS_DIALOG_GET_PRIVATE (user_data); + + if (widget == ppriv->notebook) { + if (event->keyval == GDK_Right) { + gtk_notebook_next_page (GTK_NOTEBOOK (ppriv->notebook)); + retval = TRUE; + } else if (event->keyval == GDK_Left) { + gtk_notebook_prev_page (GTK_NOTEBOOK (ppriv->notebook)); + retval = TRUE; + } + } + + return retval; +} diff --git a/src/hildon2/modest-maemo-global-settings-dialog.h b/src/hildon2/modest-maemo-global-settings-dialog.h new file mode 100644 index 0000000..351807d --- /dev/null +++ b/src/hildon2/modest-maemo-global-settings-dialog.h @@ -0,0 +1,64 @@ +/* Copyright (c) 2006, Nokia Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Nokia Corporation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __MODEST_MAEMO_GLOBAL_SETTINGS_DIALOG_H__ +#define __MODEST_MAEMO_GLOBAL_SETTINGS_DIALOG_H__ + +#include "widgets/modest-global-settings-dialog.h" + +G_BEGIN_DECLS + +/* convenience macros */ +#define MODEST_TYPE_MAEMO_GLOBAL_SETTINGS_DIALOG (modest_maemo_global_settings_dialog_get_type()) +#define MODEST_MAEMO_GLOBAL_SETTINGS_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),MODEST_TYPE_MAEMO_GLOBAL_SETTINGS_DIALOG,ModestMaemoGlobalSettingsDialog)) +#define MODEST_MAEMO_GLOBAL_SETTINGS_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),MODEST_TYPE_MAEMO_GLOBAL_SETTINGS_DIALOG,ModestMaemoGlobalSettingsDialogClass)) +#define MODEST_IS_MAEMO_GLOBAL_SETTINGS_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),MODEST_TYPE_MAEMO_GLOBAL_SETTINGS_DIALOG)) +#define MODEST_IS_MAEMO_GLOBAL_SETTINGS_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),MODEST_TYPE_MAEMO_GLOBAL_SETTINGS_DIALOG)) +#define MODEST_MAEMO_GLOBAL_SETTINGS_DIALOG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj),MODEST_TYPE_MAEMO_GLOBAL_SETTINGS_DIALOG,ModestMaemoGlobalSettingsDialogClass)) + +typedef struct _ModestMaemoGlobalSettingsDialog ModestMaemoGlobalSettingsDialog; +typedef struct _ModestMaemoGlobalSettingsDialogClass ModestMaemoGlobalSettingsDialogClass; + +struct _ModestMaemoGlobalSettingsDialog { + ModestGlobalSettingsDialog parent; +}; + +struct _ModestMaemoGlobalSettingsDialogClass { + ModestGlobalSettingsDialogClass parent_class; +}; + +/* member functions */ +GType modest_maemo_global_settings_dialog_get_type (void) G_GNUC_CONST; + +GtkWidget* modest_maemo_global_settings_dialog_new (void); + +G_END_DECLS + +#endif /* __MODEST_MAEMO_GLOBAL_SETTINGS_DIALOG_H__ */ + diff --git a/src/hildon2/modest-maemo-security-options-view.c b/src/hildon2/modest-maemo-security-options-view.c new file mode 100644 index 0000000..8593897 --- /dev/null +++ b/src/hildon2/modest-maemo-security-options-view.c @@ -0,0 +1,488 @@ +/* Copyright (c) 2008, Nokia Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Nokia Corporation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "modest-runtime.h" +#include "modest-security-options-view-priv.h" +#include "modest-maemo-security-options-view.h" +#include "modest-text-utils.h" +#include "modest-platform.h" +#include "modest-account-protocol.h" +#include "widgets/modest-ui-constants.h" +#include "widgets/modest-validating-entry.h" +#include "widgets/modest-serversecurity-combo-box.h" +#include "widgets/modest-secureauth-combo-box.h" +#include "maemo/easysetup/modest-easysetup-servertype-combo-box.h" +#ifdef MODEST_HAVE_HILDON0_WIDGETS +#include +#include +#else +#include +#include +#endif /*MODEST_HAVE_HILDON0_WIDGETS*/ + +#define PORT_MIN 1 +#define PORT_MAX 65535 + +typedef struct _ModestMaemoSecurityOptionsViewPrivate ModestMaemoSecurityOptionsViewPrivate; +struct _ModestMaemoSecurityOptionsViewPrivate { +}; + +#define MODEST_MAEMO_SECURITY_OPTIONS_VIEW_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE((o), \ + MODEST_TYPE_MAEMO_SECURITY_OPTIONS_VIEW, \ + ModestMaemoSecurityOptionsViewPrivate)) + +static void modest_maemo_security_options_view_init (ModestMaemoSecurityOptionsView *obj); +static void modest_maemo_security_options_view_finalize (GObject *obj); +static void modest_maemo_security_options_view_class_init (ModestMaemoSecurityOptionsViewClass *klass); + +G_DEFINE_TYPE (ModestMaemoSecurityOptionsView, + modest_maemo_security_options_view, + MODEST_TYPE_SECURITY_OPTIONS_VIEW); + +static void on_entry_changed (GtkEditable *editable, gpointer user_data); + +/* Tracks changes in the incoming security combo box */ +static void +on_security_changed (GtkWidget *widget, + ModestMaemoSecurityOptionsView *self) +{ + ModestSecurityOptionsViewPrivate* ppriv; + ModestServersecurityComboBox *combo; + ModestProtocolType proto_type; + ModestProtocolRegistry *proto_registry; + gboolean is_secure; + + ppriv = MODEST_SECURITY_OPTIONS_VIEW_GET_PRIVATE (self); + + proto_registry = modest_runtime_get_protocol_registry (); + combo = MODEST_SERVERSECURITY_COMBO_BOX (ppriv->security_view); + proto_type = modest_serversecurity_combo_box_get_active_serversecurity (combo); + + is_secure = modest_protocol_registry_protocol_type_has_tag (proto_registry, proto_type, + MODEST_PROTOCOL_REGISTRY_SECURE_PROTOCOLS); + + if (MODEST_SECURITY_OPTIONS_VIEW (self)->type == MODEST_SECURITY_OPTIONS_INCOMING) { + /* Activate and dim checkbutton if it's secure */ + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ppriv->auth_view), + is_secure); + gtk_widget_set_sensitive (ppriv->auth_view, !is_secure); + } else { + + } + + if (ppriv->full) { + gint port_number = + modest_serversecurity_combo_box_get_active_serversecurity_port (MODEST_SERVERSECURITY_COMBO_BOX (ppriv->security_view)); + + if(port_number) { + hildon_number_editor_set_value (HILDON_NUMBER_EDITOR (ppriv->port_view), + port_number); + } + } +} + +static void +on_auth_changed (GtkWidget *widget, + ModestMaemoSecurityOptionsView *self) +{ + ModestSecurityOptionsViewPrivate* ppriv; + ModestSecureauthComboBox *combo; + ModestProtocolRegistry *protocol_registry; + ModestProtocolType auth_proto; + gboolean secureauth_used; + GtkWidget *user_caption, *pwd_caption; + + ppriv = MODEST_SECURITY_OPTIONS_VIEW_GET_PRIVATE (self); + protocol_registry = modest_runtime_get_protocol_registry (); + combo = MODEST_SECUREAUTH_COMBO_BOX (ppriv->auth_view); + + auth_proto = modest_secureauth_combo_box_get_active_secureauth (combo); + secureauth_used = modest_protocol_registry_protocol_type_is_secure (protocol_registry, + auth_proto); + + /* Get captions, well dimm the whole widget */ + user_caption = gtk_widget_get_parent (ppriv->user_entry); + pwd_caption = gtk_widget_get_parent (ppriv->pwd_entry); + + /* Enable / disable */ + gtk_widget_set_sensitive (user_caption, secureauth_used); + gtk_widget_set_sensitive (pwd_caption, secureauth_used); + + /* Check if mandatory data is missing */ + on_entry_changed (GTK_EDITABLE (ppriv->user_entry), (gpointer) self); +} + +static void +create_incoming_security (ModestSecurityOptionsView* self, + GtkSizeGroup *size_group) +{ + ModestSecurityOptionsViewPrivate *ppriv; + GtkWidget *combo_caption, *check_caption, *entry_caption = NULL; + + ppriv = MODEST_SECURITY_OPTIONS_VIEW_GET_PRIVATE (self); + + /* Create widgets for incoming security */ + ppriv->security_view = GTK_WIDGET (modest_serversecurity_combo_box_new ()); + combo_caption = hildon_caption_new (size_group, _("mcen_li_emailsetup_secure_connection"), + ppriv->security_view, NULL, + HILDON_CAPTION_OPTIONAL); + + if (ppriv->full) { + ppriv->port_view = GTK_WIDGET (hildon_number_editor_new (PORT_MIN, PORT_MAX)); + entry_caption = hildon_caption_new (size_group, _("mcen_fi_emailsetup_port"), + ppriv->port_view, NULL, + HILDON_CAPTION_OPTIONAL); + } + + ppriv->auth_view = gtk_check_button_new (); + check_caption = + hildon_caption_new (size_group, _("mcen_li_emailsetup_secure_authentication"), + ppriv->auth_view, NULL, HILDON_CAPTION_OPTIONAL); + + /* Track changes in UI */ + g_signal_connect (G_OBJECT (ppriv->security_view), "changed", + G_CALLBACK (on_security_changed), self); + + /* Pack into container */ + gtk_box_pack_start (GTK_BOX (self), combo_caption, + FALSE, FALSE, MODEST_MARGIN_HALF); + if (ppriv->full) + gtk_box_pack_start (GTK_BOX (self), entry_caption, + FALSE, FALSE, MODEST_MARGIN_HALF); + gtk_box_pack_start (GTK_BOX (self), check_caption, + FALSE, FALSE, MODEST_MARGIN_HALF); + + /* Show widgets */ + if (ppriv->full) { + gtk_widget_show (ppriv->port_view); + gtk_widget_show (entry_caption); + } + gtk_widget_show (ppriv->security_view); + gtk_widget_show (ppriv->auth_view); + gtk_widget_show (combo_caption); + gtk_widget_show (check_caption); +} + +static void +on_entry_max (ModestValidatingEntry *self, + gpointer user_data) +{ + modest_platform_information_banner (GTK_WIDGET (self), NULL, + _CS("ckdg_ib_maximum_characters_reached")); +} + +/* + * TODO: call this whenever the auth combo changes. If we set it + * explicitely at the beggining to a value then there is no need to + * call this handler directly at the beginning + */ +static void +on_entry_changed (GtkEditable *editable, + gpointer user_data) +{ + ModestSecurityOptionsView* self; + ModestSecurityOptionsViewPrivate *ppriv; + ModestProtocolType auth_proto; + ModestSecureauthComboBox *combo; + gboolean is_secure, missing; + ModestProtocolRegistry *protocol_registry; + + self = MODEST_SECURITY_OPTIONS_VIEW (user_data); + ppriv = MODEST_SECURITY_OPTIONS_VIEW_GET_PRIVATE (self); + protocol_registry = modest_runtime_get_protocol_registry (); + + /* Outgoing username is mandatory if outgoing auth is secure */ + combo = MODEST_SECUREAUTH_COMBO_BOX (ppriv->auth_view); + auth_proto = modest_secureauth_combo_box_get_active_secureauth (combo); + is_secure = modest_protocol_registry_protocol_type_is_secure (protocol_registry, + auth_proto); + + if (is_secure && + !g_ascii_strcasecmp (gtk_entry_get_text (GTK_ENTRY (ppriv->user_entry)), "")) { + missing = TRUE; + } else { + missing = FALSE; + } + + /* Emit a signal to notify if mandatory data is missing */ + g_signal_emit_by_name (G_OBJECT (self), "missing_mandatory_data", + missing, NULL); +} + +static void +create_outgoing_security (ModestSecurityOptionsView* self, + GtkSizeGroup *size_group) +{ + ModestSecurityOptionsViewPrivate *ppriv; + GtkWidget *sec_caption, *auth_caption, *user_caption = NULL; + GtkWidget *pwd_caption = NULL, *port_caption = NULL; + + ppriv = MODEST_SECURITY_OPTIONS_VIEW_GET_PRIVATE (self); + + /* The secure connection widgets */ + ppriv->security_view = GTK_WIDGET (modest_serversecurity_combo_box_new ()); + modest_serversecurity_combo_box_fill (MODEST_SERVERSECURITY_COMBO_BOX (ppriv->security_view), + MODEST_PROTOCOLS_TRANSPORT_SMTP); + sec_caption = hildon_caption_new (size_group, _("mcen_li_emailsetup_secure_connection"), + ppriv->security_view, NULL, HILDON_CAPTION_OPTIONAL); + + /* The secure authentication widgets */ + ppriv->auth_view = GTK_WIDGET (modest_secureauth_combo_box_new ()); + auth_caption = hildon_caption_new (size_group, _("mcen_li_emailsetup_secure_authentication"), + ppriv->auth_view, NULL, HILDON_CAPTION_OPTIONAL); + + if (ppriv->full) { + gchar *user_label; + + /* Username widgets */ + ppriv->user_entry = GTK_WIDGET (modest_validating_entry_new ()); + + /* Auto-capitalization is the default, so let's turn it off: */ + hildon_gtk_entry_set_input_mode (GTK_ENTRY (ppriv->user_entry), + HILDON_GTK_INPUT_MODE_FULL); + + user_label = g_strdup_printf("%s*", _("mail_fi_username")); + user_caption = hildon_caption_new (size_group, user_label, + ppriv->user_entry, NULL, + HILDON_CAPTION_MANDATORY); + g_free (user_label); + + /* Prevent the use of some characters. Limit the max + length as well */ + modest_validating_entry_set_unallowed_characters_whitespace ( + MODEST_VALIDATING_ENTRY (ppriv->user_entry)); + gtk_entry_set_max_length (GTK_ENTRY (ppriv->user_entry), 64); + modest_validating_entry_set_max_func (MODEST_VALIDATING_ENTRY (ppriv->user_entry), + on_entry_max, self); + + /* Password widgets */ + ppriv->pwd_entry = gtk_entry_new (); + + /* Auto-capitalization is the default, so let's turn it off */ + hildon_gtk_entry_set_input_mode (GTK_ENTRY (ppriv->pwd_entry), + HILDON_GTK_INPUT_MODE_FULL | + HILDON_GTK_INPUT_MODE_INVISIBLE); + gtk_entry_set_visibility (GTK_ENTRY (ppriv->pwd_entry), FALSE); + + pwd_caption = hildon_caption_new (size_group, _("mail_fi_password"), + ppriv->pwd_entry, NULL, + HILDON_CAPTION_OPTIONAL); + + ppriv->port_view = GTK_WIDGET (hildon_number_editor_new (PORT_MIN, PORT_MAX)); + port_caption = hildon_caption_new (size_group, _("mcen_fi_emailsetup_port"), + ppriv->port_view, NULL, + HILDON_CAPTION_OPTIONAL); + } + + /* Track changes in UI */ + g_signal_connect (G_OBJECT (ppriv->security_view), "changed", + G_CALLBACK (on_security_changed), self); + if (ppriv->full) { + g_signal_connect (G_OBJECT (ppriv->auth_view), "changed", + G_CALLBACK (on_auth_changed), self); + g_signal_connect (G_OBJECT (ppriv->user_entry), "changed", + G_CALLBACK (on_entry_changed), self); + } + + /* Initialize widgets */ + modest_serversecurity_combo_box_set_active_serversecurity ( + MODEST_SERVERSECURITY_COMBO_BOX (ppriv->security_view), + MODEST_PROTOCOLS_CONNECTION_NONE); + modest_secureauth_combo_box_set_active_secureauth ( + MODEST_SECUREAUTH_COMBO_BOX (ppriv->auth_view), + MODEST_PROTOCOLS_AUTH_NONE); + + /* Pack into container */ + if (ppriv->full) { + gtk_box_pack_start (GTK_BOX (self), auth_caption, FALSE, FALSE, MODEST_MARGIN_HALF); + gtk_box_pack_start (GTK_BOX (self), user_caption, FALSE, FALSE, MODEST_MARGIN_HALF); + gtk_box_pack_start (GTK_BOX (self), pwd_caption, FALSE, FALSE, MODEST_MARGIN_HALF); + gtk_box_pack_start (GTK_BOX (self), sec_caption, FALSE, FALSE, MODEST_MARGIN_HALF); + gtk_box_pack_start (GTK_BOX (self), port_caption, FALSE, FALSE, MODEST_MARGIN_HALF); + } else { + /* The order is different */ + gtk_box_pack_start (GTK_BOX (self), sec_caption, FALSE, FALSE, MODEST_MARGIN_HALF); + gtk_box_pack_start (GTK_BOX (self), auth_caption, FALSE, FALSE, MODEST_MARGIN_HALF); + } + + /* Show widgets */ + if (ppriv->full) { + gtk_widget_show (ppriv->pwd_entry); + gtk_widget_show (ppriv->user_entry); + gtk_widget_show (ppriv->port_view); + gtk_widget_show (pwd_caption); + gtk_widget_show (user_caption); + gtk_widget_show (port_caption); + } + gtk_widget_show (ppriv->security_view); + gtk_widget_show (ppriv->auth_view); + gtk_widget_show (sec_caption); + gtk_widget_show (auth_caption); +} + +GtkWidget * +modest_maemo_security_options_view_new (ModestSecurityOptionsType type, + gboolean full, + GtkSizeGroup *size_group) +{ + ModestSecurityOptionsView* self; + ModestSecurityOptionsViewPrivate *ppriv; + + self = (ModestSecurityOptionsView *) + g_object_new (MODEST_TYPE_MAEMO_SECURITY_OPTIONS_VIEW, NULL); + ppriv = MODEST_SECURITY_OPTIONS_VIEW_GET_PRIVATE (self); + + ppriv->full = full; + self->type = type; + if (self->type == MODEST_SECURITY_OPTIONS_INCOMING) + create_incoming_security (self, size_group); + else + create_outgoing_security (self, size_group); + + return (GtkWidget *) self; +} + +static void +modest_maemo_security_options_view_load_settings (ModestSecurityOptionsView* self, + ModestAccountSettings *settings) +{ + ModestSecurityOptionsViewPrivate *ppriv; + ModestServerAccountSettings *server_settings; + gint port_number; + + ppriv = MODEST_SECURITY_OPTIONS_VIEW_GET_PRIVATE (self); + + if (self->type == MODEST_SECURITY_OPTIONS_INCOMING) + server_settings = modest_account_settings_get_store_settings (settings); + else + server_settings = modest_account_settings_get_transport_settings (settings); + port_number = modest_server_account_settings_get_port (server_settings); + + if (port_number == 0) { + /* Show the appropriate port number */ + on_security_changed (ppriv->security_view, + MODEST_MAEMO_SECURITY_OPTIONS_VIEW (self)); + } else if (ppriv->full) { + /* Keep the user-entered port-number, or the + * already-appropriate automatic port number */ + hildon_number_editor_set_value (HILDON_NUMBER_EDITOR (ppriv->port_view), + port_number); + } + /* Frees */ + g_object_unref (server_settings); +} + +static void +modest_maemo_security_options_view_save_settings (ModestSecurityOptionsView* self, + ModestAccountSettings *settings) +{ + ModestServerAccountSettings *server_settings; + ModestSecurityOptionsViewPrivate *ppriv; + gint server_port; + + ppriv = MODEST_SECURITY_OPTIONS_VIEW_GET_PRIVATE (self); + + if (self->type == MODEST_SECURITY_OPTIONS_INCOMING) + server_settings = modest_account_settings_get_store_settings (settings); + else + server_settings = modest_account_settings_get_transport_settings (settings); + + if (ppriv->full) { + server_port = hildon_number_editor_get_value (HILDON_NUMBER_EDITOR (ppriv->port_view)); + } else { + server_port = modest_serversecurity_combo_box_get_active_serversecurity_port (MODEST_SERVERSECURITY_COMBO_BOX (ppriv->security_view)); + } + + modest_server_account_settings_set_port (server_settings, server_port); + + /* Frees */ + g_object_unref (server_settings); +} + +static gboolean +modest_maemo_security_options_view_changed (ModestSecurityOptionsView* self, + ModestAccountSettings *settings) +{ + ModestServerAccountSettings *server_settings; + ModestSecurityOptionsViewPrivate *ppriv; + gint server_port; + + ppriv = MODEST_SECURITY_OPTIONS_VIEW_GET_PRIVATE (self); + + /* If we're not showing the port number then it never changes */ + if (!ppriv->full) + return FALSE; + + if (self->type == MODEST_SECURITY_OPTIONS_INCOMING) + server_settings = modest_account_settings_get_store_settings (settings); + else + server_settings = modest_account_settings_get_transport_settings (settings); + + server_port = + hildon_number_editor_get_value (HILDON_NUMBER_EDITOR (ppriv->port_view)); + + /* Frees */ + g_object_unref (server_settings); + + if (server_port != ppriv->initial_state.port) + return TRUE; + else + return FALSE; +} + +static void +modest_maemo_security_options_view_init (ModestMaemoSecurityOptionsView *obj) +{ +} + +static void +modest_maemo_security_options_view_finalize (GObject *obj) +{ + G_OBJECT_CLASS (modest_maemo_security_options_view_parent_class)->finalize (obj); +} + + +static void +modest_maemo_security_options_view_class_init (ModestMaemoSecurityOptionsViewClass *klass) +{ + GObjectClass *gobject_class = (GObjectClass*) klass; + + modest_maemo_security_options_view_parent_class = g_type_class_peek_parent (klass); + + g_type_class_add_private (gobject_class, sizeof (ModestMaemoSecurityOptionsViewPrivate)); + gobject_class->finalize = modest_maemo_security_options_view_finalize; + + MODEST_SECURITY_OPTIONS_VIEW_CLASS (klass)->load_settings = + modest_maemo_security_options_view_load_settings; + MODEST_SECURITY_OPTIONS_VIEW_CLASS (klass)->save_settings = + modest_maemo_security_options_view_save_settings; + MODEST_SECURITY_OPTIONS_VIEW_CLASS (klass)->changed = + modest_maemo_security_options_view_changed; +} diff --git a/src/hildon2/modest-maemo-security-options-view.h b/src/hildon2/modest-maemo-security-options-view.h new file mode 100644 index 0000000..9511c5c --- /dev/null +++ b/src/hildon2/modest-maemo-security-options-view.h @@ -0,0 +1,69 @@ +/* Copyright (c) 2008, Nokia Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Nokia Corporation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __MODEST_MAEMO_SECURITY_OPTIONS_VIEW_H__ +#define __MODEST_MAEMO_SECURITY_OPTIONS_VIEW_H__ + +#include +#include "modest-security-options-view.h" +#include +#include + +G_BEGIN_DECLS + +/* convenience macros */ +#define MODEST_TYPE_MAEMO_SECURITY_OPTIONS_VIEW (modest_maemo_security_options_view_get_type()) +#define MODEST_MAEMO_SECURITY_OPTIONS_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),MODEST_TYPE_MAEMO_SECURITY_OPTIONS_VIEW,ModestMaemoSecurityOptionsView)) +#define MODEST_MAEMO_SECURITY_OPTIONS_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),MODEST_TYPE_MAEMO_SECURITY_OPTIONS_VIEW,GObject)) +#define MODEST_IS_MAEMO_SECURITY_OPTIONS_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),MODEST_TYPE_MAEMO_SECURITY_OPTIONS_VIEW)) +#define MODEST_IS_MAEMO_SECURITY_OPTIONS_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),MODEST_TYPE_MAEMO_SECURITY_OPTIONS_VIEW)) +#define MODEST_MAEMO_SECURITY_OPTIONS_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj),MODEST_TYPE_MAEMO_SECURITY_OPTIONS_VIEW,ModestMaemoSecurityOptionsViewClass)) + +typedef struct _ModestMaemoSecurityOptionsView ModestMaemoSecurityOptionsView; +typedef struct _ModestMaemoSecurityOptionsViewClass ModestMaemoSecurityOptionsViewClass; + +struct _ModestMaemoSecurityOptionsView { + ModestSecurityOptionsView parent; +}; + +struct _ModestMaemoSecurityOptionsViewClass { + ModestSecurityOptionsViewClass parent_class; +}; + +/* member functions */ +GType modest_maemo_security_options_view_get_type (void) G_GNUC_CONST; + +GtkWidget* modest_maemo_security_options_view_new (ModestSecurityOptionsType type, + gboolean full, + GtkSizeGroup *size_group); + +G_END_DECLS + +#endif /* __MODEST_MAEMO_SECURITY_OPTIONS_VIEW_H__ */ + diff --git a/src/hildon2/modest-maemo-utils.c b/src/hildon2/modest-maemo-utils.c new file mode 100644 index 0000000..d1a10c7 --- /dev/null +++ b/src/hildon2/modest-maemo-utils.c @@ -0,0 +1,290 @@ +/* Copyright (c) 2006, Nokia Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Nokia Corporation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DBUS_API_SUBJECT_TO_CHANGE +#define DBUS_API_SUBJECT_TO_CHANGE +#endif /*DBUS_API_SUBJECT_TO_CHANGE*/ + +#include +#include +#include +#include +#include +#include /* for strlen */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "modest-hildon-includes.h" + +#include +#include "modest-maemo-utils.h" +#include "modest-text-utils.h" +#include "modest-platform.h" + +/* + * For getting and tracking the Bluetooth name + */ +#define BTNAME_SERVICE "org.bluez" +#define BTNAME_REQUEST_IF "org.bluez.Adapter" +#define BTNAME_SIGNAL_IF "org.bluez.Adapter" +#define BTNAME_REQUEST_PATH "/org/bluez/hci0" +#define BTNAME_SIGNAL_PATH "/org/bluez/hci0" + +#define BTNAME_REQ_GET "GetName" +#define BTNAME_SIG_CHANGED "NameChanged" + +#define BTNAME_MATCH_RULE "type='signal',interface='" BTNAME_SIGNAL_IF \ + "',member='" BTNAME_SIG_CHANGED "'" + + +static osso_context_t *__osso_context = NULL; /* urgh global */ + +osso_context_t * +modest_maemo_utils_get_osso_context (void) +{ + if (!__osso_context) + g_warning ("%s: __osso_context == NULL", __FUNCTION__); + + return __osso_context; +} + +void +modest_maemo_utils_set_osso_context (osso_context_t *osso_context) +{ + g_return_if_fail (osso_context); + __osso_context = osso_context; +} + +static void +update_device_name_from_msg (DBusMessage *message) +{ + DBusError error; + DBusMessageIter iter; + + dbus_error_init (&error); + + if (dbus_set_error_from_message (&error, message)) { + g_printerr ("modest: failed to get bluetooth name: %s\n", error.message); + dbus_error_free (&error); + } else { + const gchar *device_name; + if (!dbus_message_iter_init (message, &iter)) { + g_printerr ("modest: message did not have argument\n"); + return; + } + dbus_message_iter_get_basic (&iter, &device_name); + modest_conf_set_string (modest_runtime_get_conf(), + MODEST_CONF_DEVICE_NAME, device_name, + NULL); + } +} + + +static void +on_device_name_received (DBusPendingCall *call, void *user_data) +{ + DBusMessage *message; + + g_return_if_fail (dbus_pending_call_get_completed (call)); + + message = dbus_pending_call_steal_reply (call); + if (!message) { + g_printerr ("modest: no reply on device name query\n"); + return; + } + + update_device_name_from_msg (message); + dbus_message_unref (message); +} + + +static DBusHandlerResult +handle_dbus_signal (DBusConnection *conn, DBusMessage *msg, gpointer data) +{ + if (dbus_message_is_signal(msg, BTNAME_SIGNAL_IF, BTNAME_SIG_CHANGED)) + update_device_name_from_msg (msg); + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + + +static void +get_device_name_from_dbus () +{ + static DBusConnection *conn = NULL; + DBusMessage *request; + DBusError error; + DBusPendingCall *call = NULL; + + dbus_error_init (&error); + if (!conn) { + conn = dbus_bus_get (DBUS_BUS_SYSTEM, &error); + if (!conn) { + g_printerr ("modest: cannot get on the dbus: %s: %s\n", + error.name, error.message); + dbus_error_free (&error); + return; + } + } + + request = dbus_message_new_method_call (BTNAME_SERVICE, BTNAME_REQUEST_PATH, + BTNAME_REQUEST_IF, BTNAME_REQ_GET); + if (!request) { + /* should we free the connection? */ + g_printerr ("modest: dbus_message_new_method_call failed\n"); + return; + } + dbus_message_set_auto_start (request, TRUE); + if (dbus_connection_send_with_reply (conn, request, &call, -1)) { + dbus_pending_call_set_notify (call, on_device_name_received, + NULL, NULL); + dbus_pending_call_unref (call); + } + dbus_message_unref (request); + + dbus_connection_setup_with_g_main (conn, NULL); + dbus_bus_add_match (conn, BTNAME_MATCH_RULE, &error); + if (dbus_error_is_set(&error)) { + g_printerr ("modest: dbus_bus_add_match failed: %s\n", error.message); + dbus_error_free (&error); + } + + if (!dbus_connection_add_filter(conn, handle_dbus_signal, NULL, NULL)) + g_printerr ("modest: dbus_connection_add_filter failed\n"); +} + + +void +modest_maemo_utils_get_device_name (void) +{ + get_device_name_from_dbus (); +} + +void +modest_maemo_utils_setup_images_filechooser (GtkFileChooser *chooser) +{ + gchar *images_folder; + GtkFileFilter *file_filter; + GList *image_mimetypes_list; + GList *node; + + g_return_if_fail (GTK_IS_FILE_CHOOSER (chooser)); + + /* Set the default folder to images folder */ + images_folder = g_build_filename (g_get_home_dir (), + MODEST_MAEMO_UTILS_MYDOCS_FOLDER, + MODEST_MAEMO_UTILS_DEFAULT_IMAGE_FOLDER, NULL); + gtk_file_chooser_set_current_folder (chooser, images_folder); + g_free (images_folder); + + /* Set the images mime filter */ + file_filter = gtk_file_filter_new (); +#ifdef MODEST_HAVE_HILDON0_WIDGETS + image_mimetypes_list = osso_mime_get_mime_types_for_category (OSSO_MIME_CATEGORY_IMAGES); +#else + image_mimetypes_list = hildon_mime_get_mime_types_for_category (HILDON_MIME_CATEGORY_IMAGES); +#endif + for (node = image_mimetypes_list; node != NULL; node = g_list_next (node)) { + gtk_file_filter_add_mime_type (file_filter, node->data); + } + gtk_file_chooser_set_filter (chooser, file_filter); +#ifdef MODEST_HAVE_HILDON0_WIDGETS + osso_mime_types_list_free (image_mimetypes_list); +#else + hildon_mime_types_list_free (image_mimetypes_list); +#endif + +} + +void +modest_maemo_set_thumbable_scrollbar (GtkScrolledWindow *win, + gboolean thumbable) +{ + g_return_if_fail (GTK_IS_SCROLLED_WINDOW(win)); +#ifdef MODEST_HAVE_HILDON1_WIDGETS + hildon_helper_set_thumb_scrollbar (win, thumbable); +#endif /* MODEST_HAVE_HILDON1_WIDGETS */ +} + +FILE* +modest_maemo_open_mcc_mapping_file (void) +{ + FILE* result; + + const gchar* path; + const gchar* path1 = MODEST_OPERATOR_WIZARD_MCC_MAPPING; + const gchar* path2 = MODEST_MCC_MAPPING; + + if (access(path1, R_OK) == 0) + path = path1; + else if (access(path2, R_OK) == 0) + path = path2; + else { + g_warning ("%s: neither '%s' nor '%s' is a readable mapping file", + __FUNCTION__, path1, path2); + return NULL; + } + + result = fopen (path, "r"); + if (!result) { + g_warning ("%s: error opening mapping file '%s': %s", + __FUNCTION__, path, strerror(errno)); + return NULL; + } + return result; +} + +GtkWidget * +modest_maemo_utils_get_manager_menubar_as_menu (GtkUIManager *manager, + const gchar *item_name) +{ + GtkWidget *new_menu; + GtkWidget *menubar; + GList *children, *iter; + + menubar = gtk_ui_manager_get_widget (manager, item_name); + new_menu = gtk_menu_new (); + + children = gtk_container_get_children (GTK_CONTAINER (menubar)); + for (iter = children; iter != NULL; iter = g_list_next (iter)) { + GtkWidget *menu; + + menu = GTK_WIDGET (iter->data); + gtk_widget_reparent (menu, new_menu); + } + + g_list_free (children); + + return new_menu; +} diff --git a/src/hildon2/modest-maemo-utils.h b/src/hildon2/modest-maemo-utils.h new file mode 100644 index 0000000..c9b889b --- /dev/null +++ b/src/hildon2/modest-maemo-utils.h @@ -0,0 +1,126 @@ +/* Copyright (c) 2006, Nokia Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Nokia Corporation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef __MODEST_MAEMO_UTILS_H__ +#define __MODEST_MAEMO_UTILS_H__ + +#include +#include /* for FILE* */ +#include +#include +#include "widgets/modest-global-settings-dialog.h" +#include "widgets/modest-validating-entry.h" + +#define MODEST_MAEMO_UTILS_MYDOCS_FOLDER "MyDocs" +#define MODEST_MAEMO_UTILS_DEFAULT_IMAGE_FOLDER ".images" + + +/** + * modest_maemo_utils_get_device_name + * + * get the name for this device. Note: this queries the bluetooth + * name over DBUS, and may block. The result will be available in + * MODEST_CONF_DEVICE_NAME in ModestConf; it will be updated when it + * changes + * + */ +void modest_maemo_utils_get_device_name (void); + + +/** + * modest_maemo_utils_setup_images_filechooser: + * @chooser: a #GtkFileChooser + * + * Configures the default folder, and mime filter of a filechooser + * for images. + */ +void modest_maemo_utils_setup_images_filechooser (GtkFileChooser *chooser); + + +/** + * modest_maemo_utils_get_osso_context: + * + * get the osso_context pointer for this application + * + * Return: the osso context pointer + */ +osso_context_t *modest_maemo_utils_get_osso_context (void); + +/** + * modest_maemo_open_mcc_mapping_file: + * + * open the mcc mapping file, or NULL if it fails + * + * Returns: file ptr or NULL in case of error + */ +FILE* modest_maemo_open_mcc_mapping_file (void); + +/** + * modest_maemo_set_thumbable_scrollbar: + * @win: a scrollable window + * @thumbable: set it to thumbable (TRUE) or small (FALSE) + * + * changes the thumbability of scrollbars in a scrollable window + */ +void modest_maemo_set_thumbable_scrollbar (GtkScrolledWindow *win, gboolean thumbable); + + +/** + * modest_maemo_get_osso_context: + * + * retrieve the osso context for this application + * + * Returns: the current osso_context_t ptr + */ +osso_context_t* modest_maemo_utils_get_osso_context (void); + +/** + * modest_maemo_set_osso_context: + * + * remember the osso-context for this application + * + * @osso_context: a valid osso_context_t pointer + * + */ +void modest_maemo_utils_set_osso_context (osso_context_t *osso_context); + +/** + * modest_maemo_utils_get_manager_menubar_as_menu: + * @manager: a #GtkUIManager + * @item_name: a string + * + * obtains the node with name @item_name in @manager (which happens to be a menubar) as a + * #GtkMenu. + * + * Returns: a #GtkMenu + */ +GtkWidget *modest_maemo_utils_get_manager_menubar_as_menu (GtkUIManager *manager, const gchar *item_name); + +#endif /*__MODEST_MAEMO_UTILS_H__*/ diff --git a/src/hildon2/modest-main-window-ui-dimming.h b/src/hildon2/modest-main-window-ui-dimming.h new file mode 100644 index 0000000..eb3a8ab --- /dev/null +++ b/src/hildon2/modest-main-window-ui-dimming.h @@ -0,0 +1,128 @@ +/* Copyright (c) 2006, Nokia Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Nokia Corporation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef __MODEST_MAIN_WINDOW_UI_DIMMING_PRIV_H__ +#define __MODEST_MAIN_WINDOW_UI_DIMMING_PRIV_H__ + +#include "modest-dimming-rules-group.h" +#include "modest-ui-dimming-rules.h" + +G_BEGIN_DECLS + + +/* Menu Dimming rules entries */ +static const ModestDimmingEntry modest_main_window_menu_dimming_entries [] = { + + /* Email Menu */ + { "/MenuBar/EmailMenu/EmailNewMainMenu", NULL }, + { "/MenuBar/EmailMenu/EmailNewMainMenu/EmailNewMessageMenu", G_CALLBACK(modest_ui_dimming_rules_on_new_msg) }, + { "/MenuBar/EmailMenu/EmailNewMainMenu/EmailNewFolderMenu", G_CALLBACK(modest_ui_dimming_rules_on_new_folder) }, + { "/MenuBar/EmailMenu/EmailOpenMenu", G_CALLBACK(modest_ui_dimming_rules_on_open_msg) }, + { "/MenuBar/EmailMenu/EmailReplyMenu", G_CALLBACK(modest_ui_dimming_rules_on_reply_msg) }, + { "/MenuBar/EmailMenu/EmailReplyAllMenu", G_CALLBACK(modest_ui_dimming_rules_on_reply_msg) }, + { "/MenuBar/EmailMenu/EmailForwardMenu", G_CALLBACK(modest_ui_dimming_rules_on_reply_msg) }, + { "/MenuBar/EmailMenu/EmailContentsMenu", G_CALLBACK(modest_ui_dimming_rules_on_contents_msg) }, + { "/MenuBar/EmailMenu/EmailPurgeAttachmentsMenu", G_CALLBACK(modest_ui_dimming_rules_on_remove_attachments) }, + { "/MenuBar/EmailMenu/EmailRenameMenu", G_CALLBACK(modest_ui_dimming_rules_on_rename_folder) }, + { "/MenuBar/EmailMenu/EmailDeleteMenu", G_CALLBACK(modest_ui_dimming_rules_on_delete) }, + { "/MenuBar/EmailMenu/EmailDetailsMenu", G_CALLBACK(modest_ui_dimming_rules_on_details) }, + + /* Edit Menu */ + { "/MenuBar/EditMenu", NULL }, + { "/MenuBar/EditMenu/EditSelectAllMenu", G_CALLBACK(modest_ui_dimming_rules_on_select_all)}, + { "/MenuBar/EditMenu/EditMarkAsReadMenu", G_CALLBACK(modest_ui_dimming_rules_on_mark_as_read_msg) }, + { "/MenuBar/EditMenu/EditMarkAsUnreadMenu", G_CALLBACK(modest_ui_dimming_rules_on_mark_as_unread_msg) }, + { "/MenuBar/EditMenu/EditMoveToMenu", G_CALLBACK(modest_ui_dimming_rules_on_move_to) }, + + /* View Menu */ + { "/MenuBar/ViewMenu", NULL }, + { "/MenuBar/ViewMenu/ViewSortMenu", G_CALLBACK(modest_ui_dimming_rules_on_sort) }, + { "/MenuBar/ViewMenu/ViewToggleFoldersMenu", NULL }, + { "/MenuBar/ViewMenu/ViewToggleFullscreenMenu", NULL }, + { "/MenuBar/ViewMenu/ViewShowToolbarMainMenu", NULL }, + { "/MenuBar/ViewMenu/ViewShowToolbarMainMenu/ViewShowToolbarNormalScreenMenu", NULL }, + { "/MenuBar/ViewMenu/ViewShowToolbarMainMenu/ViewShowToolbarFullScreenMenu", NULL }, + + /* Tools Menu */ + { "/MenuBar/ToolsMenu", NULL }, + { "/MenuBar/ToolsMenu/ToolsSettingsMenu", NULL }, + { "/MenuBar/ToolsMenu/ToolsAccountsMenu", NULL }, + { "/MenuBar/ToolsMenu/ToolsSMTPServersMenu", G_CALLBACK(modest_ui_dimming_rules_on_tools_smtp_servers) }, + { "/MenuBar/ToolsMenu/ToolsSendReceiveMainMenu", NULL }, + { "/MenuBar/ToolsMenu/ToolsSendReceiveMainMenu/ToolsSendReceiveAllMenu", G_CALLBACK(modest_ui_dimming_rules_on_send_receive_all) }, + { "/MenuBar/ToolsMenu/ToolsSendReceiveMainMenu/ToolsSendReceiveCancelSendingMenu", G_CALLBACK(modest_ui_dimming_rules_on_cancel_sending) }, + { "/MenuBar/ToolsMenu/ToolsContactsMenu", NULL }, + { "/MenuBar/ToolsMenu/ToolsSearchMessagesMenu", NULL }, + { "/MenuBar/ToolsMenu/ToolsHelpMenu", NULL }, + + /* Close Menu */ + { "/MenuBar/CloseMenu", NULL }, + { "/MenuBar/ToolsMenu/CloseWindowMenu", NULL }, + { "/MenuBar/ToolsMenu/CloseAllWindowsMenu", NULL }, + + /* Contextual Menus (Header View) */ + { "/HeaderViewCSM/HeaderViewCSMOpen", G_CALLBACK(modest_ui_dimming_rules_on_open_msg) }, + { "/HeaderViewCSM/HeaderViewCSMReply", G_CALLBACK(modest_ui_dimming_rules_on_reply_msg) }, + { "/HeaderViewCSM/HeaderViewCSMReplyAll", G_CALLBACK(modest_ui_dimming_rules_on_reply_msg) }, + { "/HeaderViewCSM/HeaderViewCSMForward", G_CALLBACK(modest_ui_dimming_rules_on_reply_msg) }, + { "/HeaderViewCSM/HeaderViewCSMDelete", G_CALLBACK(modest_ui_dimming_rules_on_delete_msg) }, + { "/HeaderViewCSM/HeaderViewCSMCancelSending", G_CALLBACK(modest_ui_dimming_rules_on_cancel_sending) }, + { "/HeaderViewCSM/HeaderViewCSMHelp", NULL }, + + /* Contextual Menus (Folder View) */ + { "/FolderViewCSM/FolderViewCSMNewFolder", G_CALLBACK(modest_ui_dimming_rules_on_new_folder) }, + { "/FolderViewCSM/FolderViewCSMRenameFolder", G_CALLBACK(modest_ui_dimming_rules_on_rename_folder) }, + { "/FolderViewCSM/FolderViewCSMPasteMsgs", G_CALLBACK(modest_ui_dimming_rules_on_paste) }, + { "/FolderViewCSM/FolderViewCSMDeleteFolder", G_CALLBACK(modest_ui_dimming_rules_on_delete_folder) }, + { "/FolderViewCSM/FolderViewCSMSearchMessages", NULL }, + { "/FolderViewCSM/FolderViewCSMHelp", NULL }, + + /* Contextual Menus (Toolbar) */ + { "/ToolbarReplyCSM/ToolbarMessageForward", NULL }, + { "/ToolbarReplyCSM/ToolbarMessageReplyAll", NULL }, + { "/ToolbarReplyCSM/ToolbarMessageReply", NULL }, + +}; + +/* Toolbar Dimming rules entries */ +static const ModestDimmingEntry modest_main_window_toolbar_dimming_entries [] = { + + /* Toolbar */ + { "/ToolBar/ToolbarMessageNew", G_CALLBACK(modest_ui_dimming_rules_on_new_msg) }, + { "/ToolBar/ToolbarMessageReply", G_CALLBACK(modest_ui_dimming_rules_on_reply_msg) }, + { "/ToolBar/ToolbarDeleteMessage", G_CALLBACK(modest_ui_dimming_rules_on_delete) }, + { "/ToolBar/ToolbarToggleView", NULL }, + { "/ToolBar/ToolbarSort", G_CALLBACK(modest_ui_dimming_rules_on_sort) }, + { "/ToolBar/ToolbarSendReceive", G_CALLBACK(modest_ui_dimming_rules_on_send_receive) }, + { "/ToolBar/ToolbarCancel", NULL }, +}; + +G_END_DECLS +#endif /* __MODEST_MAIN_WINDOW_UI_PRIV_H__ */ diff --git a/src/hildon2/modest-main-window-ui.h b/src/hildon2/modest-main-window-ui.h new file mode 100644 index 0000000..9bbe466 --- /dev/null +++ b/src/hildon2/modest-main-window-ui.h @@ -0,0 +1,136 @@ +/* Copyright (c) 2006, Nokia Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Nokia Corporation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __MODEST_MAIN_WINDOW_UI_PRIV_H__ +#define __MODEST_MAIN_WINDOW_UI_PRIV_H__ + +#include + +#include "modest-icon-names.h" +#include "modest-ui-actions.h" + +G_BEGIN_DECLS + + +/* Action entries */ +static const GtkActionEntry modest_action_entries [] = { + + /* Toplevel menus */ + { "Email", NULL, N_("mcen_me_inbox_email"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_email_menu_activated) }, + { "Edit", NULL, N_("mcen_me_inbox_edit"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_edit_menu_activated) }, + { "View", NULL, N_("mcen_me_inbox_view"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_view_menu_activated) }, + { "Tools", NULL, N_("mcen_me_inbox_tools"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_tools_menu_activated) }, + { "Attachments", NULL, N_("mcen_me_viewer_attachments"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_attachment_menu_activated) }, + { "Close", NULL, N_("mcen_me_inbox_close") }, + { "Zoom", NULL, N_("mcen_me_viewer_zoom") }, + + /* Zoom and fullscreen keyboard actions*/ + { "ZoomPlus", NULL, N_("Zoom +"), "F7", NULL, G_CALLBACK (modest_ui_actions_on_zoom_plus) }, + { "ZoomMinus", NULL, N_("Zoom -"), "F8", NULL, G_CALLBACK (modest_ui_actions_on_zoom_minus) }, + { "ToggleFullscreen", NULL, N_("mcen_me_inbox_fullscreen"), "F6", NULL, G_CALLBACK (modest_ui_actions_on_change_fullscreen) }, + + /* Email */ + { "EmailNew", NULL, N_("mcen_me_inbox_new") }, /* submenu */ + { "EmailNewMessage", NULL, N_("mcen_me_inbox_message"), "N", NULL, G_CALLBACK (modest_ui_actions_on_new_msg) }, + { "EmailNewDefault", NULL, N_("mcen_me_viewer_newemail"), "N", NULL, G_CALLBACK (modest_ui_actions_on_new_msg) }, + { "EmailNewFolder", NULL, N_("mcen_me_inbox_folder"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_new_folder) }, + { "EmailOpen", NULL, N_("mcen_me_inbox_open"), "O", NULL, G_CALLBACK (modest_ui_actions_on_open) }, + { "EmailReply", NULL, N_("mcen_me_inbox_reply"), "R", NULL, G_CALLBACK (modest_ui_actions_on_reply) }, + { "EmailReplyAll", NULL, N_("mcen_me_inbox_replytoall"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_reply_all) }, + { "EmailForward", NULL, N_("mcen_me_inbox_forward"), "D", NULL, G_CALLBACK (modest_ui_actions_on_forward) }, + { "EmailRenameFolder", NULL, N_("mcen_me_user_renamefolder"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_rename_folder) }, + { "EmailDelete", NULL, N_("mcen_me_inbox_delete"), "BackSpace", NULL, G_CALLBACK (modest_ui_actions_on_delete_message_or_folder) }, + { "EmailContents", NULL, N_("mcen_me_inbox_retrieve_contents"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_retrieve_msg_contents) }, + { "EmailDetails", NULL, N_("mcen_me_inbox_messagedetails"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_details) }, + { "EmailPurgeAttachments", NULL, N_("mcen_me_inbox_remove_attachments"), NULL, NULL, G_CALLBACK (modest_ui_actions_remove_attachments) }, + + + /* Edit */ + { "EditCut", NULL, N_("mcen_me_inbox_cut"), "X", NULL, G_CALLBACK (modest_ui_actions_on_cut) }, + { "EditCopy", NULL, N_("mcen_me_inbox_copy"), "C", NULL, G_CALLBACK (modest_ui_actions_on_copy) }, + { "EditPaste", NULL, N_("mcen_me_inbox_paste"), "V", NULL, G_CALLBACK (modest_ui_actions_on_paste) }, + { "EditSelectAll", NULL, N_("mcen_me_viewer_selectall"), "A", NULL, G_CALLBACK (modest_ui_actions_on_select_all) }, + { "EditMarkAsRead", NULL, N_("mcen_me_inbox_mark_as_read"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_mark_as_read) }, + { "EditMarkAsUnread", NULL, N_("mcen_me_inbox_mark_as_unread"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_mark_as_unread) }, + { "EditMoveTo", NULL, N_("mcen_me_inbox_moveto"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_move_to) }, + + /* View */ + { "ViewSort", NULL, N_("mcen_me_inbox_sort"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_sort) }, + { "ViewShowToolbar", NULL, N_("mcen_me_inbox_toolbar") }, /* submenu */ + { "ViewPreviousMessage", NULL, N_("mcen_me_viewer_previousmessage"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_prev) }, + { "ViewNextMessage", NULL, N_("mcen_me_viewer_nextmessage"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_next) }, + + /* Attachments */ + { "ViewAttachment", NULL, N_("mcen_me_viewer_view_attachment"), NULL, NULL, G_CALLBACK (modest_ui_actions_view_attachment) }, + { "SaveAttachment", NULL, N_("mcen_me_viewer_save_attachments"), NULL, NULL, G_CALLBACK (modest_ui_actions_save_attachments) }, + { "RemoveAttachment", NULL, N_("mcen_me_inbox_remove_attachments"), NULL, NULL, G_CALLBACK (modest_ui_actions_remove_attachments) }, + + /* Tools */ + { "ToolsSettings", NULL, N_("mcen_me_inbox_options"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_settings) }, + { "ToolsAccounts", NULL, N_("mcen_me_inbox_accounts"), NULL, NULL, G_CALLBACK(modest_ui_actions_on_accounts) }, + { "ToolsSMTPServers", NULL, N_("mcen_me_inbox_globalsmtpservers"), NULL, NULL, G_CALLBACK(modest_ui_actions_on_smtp_servers) }, + { "ToolsSendReceive", NULL, N_("mcen_me_inbox_sendandreceive") }, /* submenu */ + { "ToolsSendReceiveAll", NULL, N_("mcen_me_inbox_sendandreceive_all"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_send_receive) }, + { "ToolsSendReceiveCancelSending", NULL, N_("mcen_me_outbox_cancelsend"), NULL, NULL, G_CALLBACK (modest_ui_actions_cancel_send) }, + { "ToolsContacts", NULL, N_("mcen_me_inbox_open_addressbook"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_open_addressbook) }, + { "ToolsAddToContacts", NULL, N_("mcen_me_viewer_addtocontacts"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_add_to_contacts) }, + { "ToolsSearchMessages", NULL, N_("mcen_me_inbox_search"), "E", NULL, G_CALLBACK (modest_ui_actions_on_search_messages) }, + { "ToolsHelp", NULL, N_("mcen_me_inbox_help"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_help) }, + + /* Close */ + { "CloseWindow", NULL, N_("mcen_me_inbox_close_window"), "W", NULL, G_CALLBACK (modest_ui_actions_on_close_window) }, + { "CloseAllWindows", NULL, N_("mcen_me_inbox_close_windows"), "Q", NULL, G_CALLBACK (modest_ui_actions_on_quit) }, + + + /* Toolbar items; they is some overlap with the menu items, + * but we need to specificy them differently, they have icons for example + */ + /* Headers Toolbar */ + { "ToolbarMessageNew", MODEST_STOCK_NEW_MAIL, N_("qgn_toolb_messagin_new"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_new_msg) }, + { "ToolbarMessageReply", MODEST_STOCK_REPLY, N_("mcen_me_inbox_reply"), "R", NULL, G_CALLBACK (modest_ui_actions_on_reply) }, + { "ToolbarMessageReplyAll", MODEST_STOCK_REPLY_ALL, N_("mcen_me_inbox_replytoall"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_reply_all) }, + { "ToolbarMessageForward", MODEST_STOCK_FORWARD, N_("mcen_me_inbox_forward"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_forward) }, + { "ToolbarSendReceive", MODEST_STOCK_REFRESH, N_("qgn_toolb_gene_refresh"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_send_receive) }, + { "ToolbarDeleteMessage", MODEST_STOCK_DELETE, N_("qgn_toolb_gene_deletebutton"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_delete_message_or_folder) }, + { "ToolbarSort", MODEST_STOCK_SORT, N_("qgn_list_sort"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_sort) }, + { "ToolbarFindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), NULL, NULL, NULL }, + { "ToolbarMessageBack", MODEST_TOOLBAR_ICON_PREV, N_("qgn_toolb_gene_back"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_prev) }, + { "ToolbarMessageNext", MODEST_TOOLBAR_ICON_NEXT, N_("qgn_toolb_gene_forward"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_next) }, + { "ToolbarMessageMoveTo", MODEST_TOOLBAR_ICON_MOVE_TO_FOLDER, N_("qgn_toolb_gene_movetofldr"), NULL, NULL, G_CALLBACK(modest_ui_actions_on_move_to) }, + { "ToolbarCancel", GTK_STOCK_STOP, "", NULL, NULL, NULL }, +}; + +static const GtkToggleActionEntry modest_toggle_action_entries [] = { + { "ViewToggleFullscreen", GTK_STOCK_FULLSCREEN, N_("mcen_me_inbox_fullscreen"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_toggle_fullscreen), FALSE }, + { "ViewShowToolbarNormalScreen", NULL, N_("mcen_me_inbox_normalview"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_toggle_toolbar), TRUE }, + { "ViewShowToolbarFullScreen", NULL, N_("mcen_me_inbox_fullscreen"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_toggle_toolbar), TRUE }, +}; + +G_END_DECLS +#endif /* __MODEST_MAIN_WINDOW_UI_PRIV_H__ */ diff --git a/src/hildon2/modest-main-window.c b/src/hildon2/modest-main-window.c new file mode 100644 index 0000000..e252f7f --- /dev/null +++ b/src/hildon2/modest-main-window.c @@ -0,0 +1,3104 @@ +/* Copyright (c) 2006, Nokia Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Nokia Corporation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "modest-hildon-includes.h" +#include "modest-defs.h" +#include +#include "widgets/modest-header-view-priv.h" +#include "widgets/modest-main-window.h" +#include "widgets/modest-msg-edit-window.h" +#include "widgets/modest-account-view-window.h" +#include "modest-runtime.h" +#include "modest-account-mgr-helpers.h" +#include "modest-platform.h" +#include "modest-widget-memory.h" +#include "modest-window-priv.h" +#include "modest-main-window-ui.h" +#include "modest-main-window-ui-dimming.h" +#include "modest-account-mgr.h" +#include "modest-tny-account.h" +#include "modest-tny-folder.h" +#include "modest-conf.h" +#include +#include +#include "modest-tny-platform-factory.h" +#include "modest-tny-msg.h" +#include "modest-mail-operation.h" +#include "modest-icon-names.h" +#include "modest-progress-bar.h" +#include "modest-text-utils.h" +#include "modest-ui-dimming-manager.h" +#include "maemo/modest-osso-state-saving.h" +#include "modest-text-utils.h" +#include "modest-signal-mgr.h" + +#define MODEST_MAIN_WINDOW_ACTION_GROUP_ADDITIONS "ModestMainWindowActionAdditions" + +#define XALIGN 0.5 +#define YALIGN 0.0 +#define XSPACE 1 +#define YSPACE 0 + +/* 'private'/'protected' functions */ +static void modest_main_window_class_init (ModestMainWindowClass *klass); +static void modest_main_window_init (ModestMainWindow *obj); +static void modest_main_window_finalize (GObject *obj); + +static gboolean modest_main_window_window_state_event (GtkWidget *widget, + GdkEventWindowState *event, + gpointer userdata); + +static void connect_signals (ModestMainWindow *self); + +static void modest_main_window_disconnect_signals (ModestWindow *self); + +static void restore_settings (ModestMainWindow *self, + gboolean do_folder_view_too); + +static void save_state (ModestWindow *self); + +static void update_menus (ModestMainWindow* self); + +static void modest_main_window_show_toolbar (ModestWindow *window, + gboolean show_toolbar); + +static void cancel_progressbar (GtkToolButton *toolbutton, + ModestMainWindow *self); + +static void on_queue_changed (ModestMailOperationQueue *queue, + ModestMailOperation *mail_op, + ModestMailOperationQueueNotification type, + ModestMainWindow *self); + +static gboolean on_zoom_minus_plus_not_implemented (ModestWindow *window); + +static void on_account_inserted (TnyAccountStore *accoust_store, + TnyAccount *account, + gpointer user_data); + +static void on_account_removed (TnyAccountStore *accoust_store, + TnyAccount *account, + gpointer user_data); + +static void on_account_changed (TnyAccountStore *account_store, + TnyAccount *account, + gpointer user_data); + +static void on_default_account_changed (ModestAccountMgr* mgr, + gpointer user_data); + +static gboolean on_inner_widgets_key_pressed (GtkWidget *widget, + GdkEventKey *event, + gpointer user_data); + +static void on_configuration_key_changed (ModestConf* conf, + const gchar *key, + ModestConfEvent event, + ModestConfNotificationId id, + ModestMainWindow *self); + +static void set_toolbar_mode (ModestMainWindow *self, + ModestToolBarModes mode); + +static gboolean set_toolbar_transfer_mode (ModestMainWindow *self); + +static void on_show_account_action_toggled (GtkToggleAction *action, + gpointer user_data); + +static void on_refresh_account_action_activated (GtkAction *action, + gpointer user_data); + +static void on_send_receive_csm_activated (GtkMenuItem *item, + gpointer user_data); + +static void on_msg_count_changed (ModestHeaderView *header_view, + TnyFolder *folder, + TnyFolderChange *change, + ModestMainWindow *main_window); + +static void modest_main_window_cleanup_queue_error_signals (ModestMainWindow *self); + + +static GtkWidget * create_empty_view (void); + +static gboolean on_folder_view_focus_in (GtkWidget *widget, + GdkEventFocus *event, + gpointer userdata); + +static gboolean on_header_view_focus_in (GtkWidget *widget, + GdkEventFocus *event, + gpointer userdata); + +static void on_folder_selection_changed (ModestFolderView *folder_view, + TnyFolderStore *folder_store, + gboolean selected, + ModestMainWindow *main_window); + +static void set_at_least_one_account_visible(ModestMainWindow *self); + +static void on_updating_msg_list (ModestHeaderView *header_view, + gboolean starting, + gpointer user_data); + +static gboolean restore_paned_timeout_handler (gpointer *data); + +static gboolean show_opening_banner (gpointer user_data); + +static void on_window_destroy (GtkObject *widget, + gpointer userdata); + +static void on_window_hide (GObject *gobject, + GParamSpec *arg1, + gpointer user_data); + +typedef struct _ModestMainWindowPrivate ModestMainWindowPrivate; +struct _ModestMainWindowPrivate { + GtkWidget *msg_paned; + GtkWidget *main_paned; + GtkWidget *main_vbox; + GtkWidget *contents_widget; + GtkWidget *empty_view; + + /* Progress observers */ + GtkWidget *progress_bar; + GSList *progress_widgets; + + /* Tollbar items */ + GtkWidget *progress_toolitem; + GtkWidget *cancel_toolitem; + GtkWidget *sort_toolitem; + GtkWidget *refresh_toolitem; + ModestToolBarModes current_toolbar_mode; + + /* Merge ids used to add/remove accounts to the ViewMenu*/ + GByteArray *merge_ids; + GtkActionGroup *view_additions_group; + + /* On-demand widgets */ + GtkWidget *accounts_popup; + GtkWidget *details_widget; + + /* Optimized view enabled */ + gboolean optimized_view; + + /* Optimized view enabled */ + gboolean send_receive_in_progress; + + ModestHeaderView *header_view; + ModestFolderView *folder_view; + + ModestMainWindowStyle style; + ModestMainWindowContentsStyle contents_style; + gboolean wait_for_settings; + + guint progress_bar_timeout; + guint restore_paned_timeout; + + /* Signal handler UIDs */ + GList *queue_err_signals; + GSList *sighandlers; + + /* "Updating" banner for header view */ + GtkWidget *updating_banner; + guint updating_banner_timeout; + + /* "Opening" banner for header view */ + GtkWidget *opening_banner; + guint opening_banner_timeout; + + /* Display state */ + osso_display_state_t display_state; +}; +#define MODEST_MAIN_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \ + MODEST_TYPE_MAIN_WINDOW, \ + ModestMainWindowPrivate)) + +typedef struct _GetMsgAsyncHelper { + ModestMainWindowPrivate *main_window_private; + guint action; + ModestTnyMsgReplyType reply_type; + ModestTnyMsgForwardType forward_type; + gchar *from; + TnyIterator *iter; +} GetMsgAsyncHelper; + + +/* globals */ +static GtkWindowClass *parent_class = NULL; + + +/* Private actions */ +/* This is the context sensitive menu: */ +static const GtkActionEntry modest_folder_view_action_entries [] = { + + /* Folder View CSM actions */ + { "FolderViewCSMNewFolder", NULL, N_("mcen_ti_new_folder"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_new_folder) }, + { "FolderViewCSMRenameFolder", NULL, N_("mcen_me_user_renamefolder"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_rename_folder) }, + { "FolderViewCSMPasteMsgs", NULL, N_("mcen_me_inbox_paste"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_paste)}, + { "FolderViewCSMDeleteFolder", NULL, N_("mcen_me_inbox_delete"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_delete_folder) }, + { "FolderViewCSMSearchMessages", NULL, N_("mcen_me_inbox_search"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_search_messages) }, + { "FolderViewCSMHelp", NULL, N_("mcen_me_inbox_help"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_csm_help) }, +}; + +static const GtkActionEntry modest_header_view_action_entries [] = { + + /* Header View CSM actions */ + { "HeaderViewCSMOpen", NULL, N_("mcen_me_inbox_open"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_open) }, + { "HeaderViewCSMReply", NULL, N_("mcen_me_inbox_reply"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_reply) }, + { "HeaderViewCSMReplyAll", NULL, N_("mcen_me_inbox_replytoall"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_reply_all) }, + { "HeaderViewCSMForward", NULL, N_("mcen_me_inbox_forward"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_forward) }, + { "HeaderViewCSMCut", NULL, N_("mcen_me_inbox_cut"), "X", NULL, G_CALLBACK (modest_ui_actions_on_cut) }, + { "HeaderViewCSMCopy", NULL, N_("mcen_me_inbox_copy"), "C", NULL, G_CALLBACK (modest_ui_actions_on_copy) }, + { "HeaderViewCSMPaste", NULL, N_("mcen_me_inbox_paste"), "V", NULL, G_CALLBACK (modest_ui_actions_on_paste) }, + { "HeaderViewCSMDelete", NULL, N_("mcen_me_inbox_delete"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_delete_message) }, + { "HeaderViewCSMCancelSending", NULL, N_("mcen_me_outbox_cancelsend"), NULL, NULL, G_CALLBACK (modest_ui_actions_cancel_send) }, + { "HeaderViewCSMHelp", NULL, N_("mcen_me_inbox_help"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_help) }, +}; + +static const GtkToggleActionEntry modest_main_window_toggle_action_entries [] = { + { "ToggleFolders", MODEST_STOCK_SPLIT_VIEW, N_("mcen_me_inbox_hidefolders"), "t", NULL, G_CALLBACK (modest_ui_actions_toggle_folders_view), TRUE }, +}; + +/************************************************************************/ + +GType +modest_main_window_get_type (void) +{ + static GType my_type = 0; + if (!my_type) { + static const GTypeInfo my_info = { + sizeof(ModestMainWindowClass), + NULL, /* base init */ + NULL, /* base finalize */ + (GClassInitFunc) modest_main_window_class_init, + NULL, /* class finalize */ + NULL, /* class data */ + sizeof(ModestMainWindow), + 1, /* n_preallocs */ + (GInstanceInitFunc) modest_main_window_init, + NULL + }; + my_type = g_type_register_static (MODEST_TYPE_WINDOW, + "ModestMainWindow", + &my_info, 0); + } + return my_type; +} + +static void +modest_main_window_class_init (ModestMainWindowClass *klass) +{ + GObjectClass *gobject_class; + gobject_class = (GObjectClass*) klass; + ModestWindowClass *modest_window_class = (ModestWindowClass *) klass; + + parent_class = g_type_class_peek_parent (klass); + gobject_class->finalize = modest_main_window_finalize; + + g_type_class_add_private (gobject_class, sizeof(ModestMainWindowPrivate)); + + modest_window_class->show_toolbar_func = modest_main_window_show_toolbar; + modest_window_class->save_state_func = save_state; + modest_window_class->zoom_minus_func = on_zoom_minus_plus_not_implemented; + modest_window_class->zoom_plus_func = on_zoom_minus_plus_not_implemented; + modest_window_class->disconnect_signals_func = modest_main_window_disconnect_signals; +} + +static void +modest_main_window_init (ModestMainWindow *obj) +{ + ModestMainWindowPrivate *priv; + + priv = MODEST_MAIN_WINDOW_GET_PRIVATE(obj); + + priv->queue_err_signals = NULL; + priv->msg_paned = NULL; + priv->main_paned = NULL; + priv->main_vbox = NULL; + priv->header_view = NULL; + priv->folder_view = NULL; + priv->contents_widget = NULL; + priv->accounts_popup = NULL; + priv->details_widget = NULL; + priv->empty_view = NULL; + priv->progress_widgets = NULL; + priv->progress_bar = NULL; + priv->current_toolbar_mode = TOOLBAR_MODE_NORMAL; + priv->style = MODEST_MAIN_WINDOW_STYLE_SPLIT; + priv->wait_for_settings = TRUE; + priv->contents_style = -1; /* invalid contents style. We need this to select it for the first time */ + priv->merge_ids = NULL; + priv->optimized_view = FALSE; + priv->send_receive_in_progress = FALSE; + priv->progress_bar_timeout = 0; + priv->restore_paned_timeout = 0; + priv->sighandlers = NULL; + priv->updating_banner = NULL; + priv->updating_banner_timeout = 0; + priv->opening_banner = NULL; + priv->opening_banner_timeout = 0; + priv->display_state = OSSO_DISPLAY_ON; + + modest_window_mgr_register_help_id (modest_runtime_get_window_mgr(), + GTK_WINDOW(obj), + "applications_email_mainview"); +} + +static void +modest_main_window_finalize (GObject *obj) +{ + ModestMainWindowPrivate *priv; + + priv = MODEST_MAIN_WINDOW_GET_PRIVATE(obj); + + /* Sanity check: shouldn't be needed, the window mgr should + call this function before */ + modest_main_window_disconnect_signals (MODEST_WINDOW (obj)); + modest_main_window_cleanup_queue_error_signals ((ModestMainWindow *) obj); + + if (priv->empty_view) { + g_object_unref (priv->empty_view); + priv->empty_view = NULL; + } + + if (priv->header_view) { + g_object_unref (priv->header_view); + priv->header_view = NULL; + } + + g_slist_free (priv->progress_widgets); + + g_byte_array_free (priv->merge_ids, TRUE); + + if (priv->progress_bar_timeout > 0) { + g_source_remove (priv->progress_bar_timeout); + priv->progress_bar_timeout = 0; + } + + if (priv->updating_banner_timeout > 0) { + g_source_remove (priv->updating_banner_timeout); + priv->updating_banner_timeout = 0; + } + + if (priv->updating_banner) { + gtk_widget_destroy (priv->updating_banner); + priv->updating_banner = NULL; + } + + if (priv->opening_banner_timeout > 0) { + g_source_remove (priv->opening_banner_timeout); + priv->opening_banner_timeout = 0; + } + + if (priv->opening_banner) { + gtk_widget_destroy (priv->opening_banner); + priv->opening_banner = NULL; + } + + if (priv->restore_paned_timeout > 0) { + g_source_remove (priv->restore_paned_timeout); + priv->restore_paned_timeout = 0; + } + + G_OBJECT_CLASS(parent_class)->finalize (obj); +} + +GtkWidget* +modest_main_window_get_child_widget (ModestMainWindow *self, + ModestMainWindowWidgetType widget_type) +{ + ModestMainWindowPrivate *priv; + GtkWidget *widget; + + g_return_val_if_fail (self && MODEST_IS_MAIN_WINDOW(self), NULL); + g_return_val_if_fail (widget_type >= 0 && widget_type < MODEST_MAIN_WINDOW_WIDGET_TYPE_NUM, + NULL); + + priv = MODEST_MAIN_WINDOW_GET_PRIVATE(self); + + switch (widget_type) { + case MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW: + widget = (GtkWidget*)priv->header_view; break; + case MODEST_MAIN_WINDOW_WIDGET_TYPE_FOLDER_VIEW: + widget = (GtkWidget*)priv->folder_view; break; + default: + return NULL; + } + + /* Note that the window could have been destroyed, and so + their children, but still have some references */ + return (widget && GTK_IS_WIDGET(widget)) ? GTK_WIDGET(widget) : NULL; +} + +static gboolean +restore_paned_timeout_handler (gpointer *data) +{ + ModestMainWindow *main_window = MODEST_MAIN_WINDOW (data); + ModestMainWindowPrivate *priv = MODEST_MAIN_WINDOW_GET_PRIVATE (main_window); + ModestConf *conf; + + /* Timeouts are outside the main lock */ + gdk_threads_enter (); + if (GTK_WIDGET_VISIBLE (main_window)) { + conf = modest_runtime_get_conf (); + modest_widget_memory_restore (conf, G_OBJECT(priv->main_paned), + MODEST_CONF_MAIN_PANED_KEY); + } + gdk_threads_leave (); + + return FALSE; +} + + +static void +restore_settings (ModestMainWindow *self, gboolean do_folder_view_too) +{ + ModestConf *conf; + ModestMainWindowPrivate *priv; + + priv = MODEST_MAIN_WINDOW_GET_PRIVATE(self); + + conf = modest_runtime_get_conf (); + + modest_widget_memory_restore (conf, G_OBJECT(self), + MODEST_CONF_MAIN_WINDOW_KEY); + + modest_widget_memory_restore (conf, G_OBJECT(priv->header_view), + MODEST_CONF_HEADER_VIEW_KEY); + + if (do_folder_view_too) + modest_widget_memory_restore (conf, G_OBJECT(priv->folder_view), + MODEST_CONF_FOLDER_VIEW_KEY); + +/* modest_widget_memory_restore (conf, G_OBJECT(priv->main_paned), */ +/* MODEST_CONF_MAIN_PANED_KEY); */ + + g_timeout_add (250, (GSourceFunc) restore_paned_timeout_handler, self); + + /* We need to force a redraw here in order to get the right + position of the horizontal paned separator */ + gtk_widget_show (GTK_WIDGET (self)); +} + + +static void +save_state (ModestWindow *window) +{ + ModestConf *conf; + ModestMainWindow* self = MODEST_MAIN_WINDOW(window); + ModestMainWindowPrivate *priv; + + priv = MODEST_MAIN_WINDOW_GET_PRIVATE(self); + conf = modest_runtime_get_conf (); + + modest_widget_memory_save (conf,G_OBJECT(self), + MODEST_CONF_MAIN_WINDOW_KEY); + /* Only save main paned position if we're in split mode */ + if (priv->style == MODEST_MAIN_WINDOW_STYLE_SPLIT) + modest_widget_memory_save (conf, G_OBJECT(priv->main_paned), + MODEST_CONF_MAIN_PANED_KEY); + modest_widget_memory_save (conf, G_OBJECT(priv->folder_view), + MODEST_CONF_FOLDER_VIEW_KEY); +} + +static gint +compare_display_names (ModestAccountSettings *a, + ModestAccountSettings *b) +{ + return g_utf8_collate (modest_account_settings_get_display_name (a), + modest_account_settings_get_display_name (b)); + +} + +/* We use this function to prevent the send&receive CSM to be shown + when there are less than two account */ +static gboolean +tap_and_hold_query_cb (GtkWidget *widget, GdkEvent *event) +{ + return TRUE; +} + +static void +update_menus (ModestMainWindow* self) +{ + GSList *account_names, *iter, *accounts; + ModestMainWindowPrivate *priv; + ModestWindowPrivate *parent_priv; + ModestAccountMgr *mgr; + gint i, num_accounts; + GList *groups; + gchar *default_account; + const gchar *active_account_name; + GtkWidget *send_receive_button, *item; + GtkAction *send_receive_all = NULL; + GSList *radio_group; + + priv = MODEST_MAIN_WINDOW_GET_PRIVATE (self); + parent_priv = MODEST_WINDOW_GET_PRIVATE (self); + + /* Get enabled account IDs */ + mgr = modest_runtime_get_account_mgr (); + account_names = modest_account_mgr_account_names (mgr, TRUE); + iter = account_names; + accounts = NULL; + + while (iter) { + ModestAccountSettings *settings = + modest_account_mgr_load_account_settings (mgr, (gchar*) iter->data); + accounts = g_slist_prepend (accounts, settings); + + iter = iter->next; + } + modest_account_mgr_free_account_names (account_names); + account_names = NULL; + + /* Order the list of accounts by its display name */ + accounts = g_slist_sort (accounts, (GCompareFunc) compare_display_names); + num_accounts = g_slist_length (accounts); + + send_receive_all = gtk_ui_manager_get_action (parent_priv->ui_manager, + "/MenuBar/ToolsMenu/ToolsSendReceiveMainMenu/ToolsSendReceiveAllMenu"); + gtk_action_set_visible (send_receive_all, num_accounts > 0); + + /* Delete old send&receive popup items. We can not just do a + menu_detach because it does not work well with + tap_and_hold */ + if (priv->accounts_popup) + gtk_container_foreach (GTK_CONTAINER (priv->accounts_popup), + (GtkCallback) gtk_widget_destroy, NULL); + + /* Delete old entries in the View menu. Do not free groups, it + belongs to Gtk+ */ + groups = gtk_ui_manager_get_action_groups (parent_priv->ui_manager); + while (groups) { + if (!strcmp (MODEST_MAIN_WINDOW_ACTION_GROUP_ADDITIONS, + gtk_action_group_get_name (GTK_ACTION_GROUP (groups->data)))) { + gtk_ui_manager_remove_action_group (parent_priv->ui_manager, + GTK_ACTION_GROUP (groups->data)); + groups = NULL; + /* Remove uis */ + if (priv->merge_ids) { + for (i = 0; i < priv->merge_ids->len; i++) + gtk_ui_manager_remove_ui (parent_priv->ui_manager, priv->merge_ids->data[i]); + g_byte_array_free (priv->merge_ids, TRUE); + } + /* We need to call this in order to ensure + that the new actions are added in the right + order (alphabetical) */ + gtk_ui_manager_ensure_update (parent_priv->ui_manager); + } else + groups = g_list_next (groups); + } + priv->merge_ids = g_byte_array_sized_new (num_accounts); + + /* Get send receive button */ + send_receive_button = gtk_ui_manager_get_widget (parent_priv->ui_manager, + "/ToolBar/ToolbarSendReceive"); + + /* Create the menu */ + if (num_accounts > 1) { + if (!priv->accounts_popup) + priv->accounts_popup = gtk_menu_new (); + item = gtk_menu_item_new_with_label (_("mcen_me_toolbar_sendreceive_all")); + gtk_menu_shell_append (GTK_MENU_SHELL (priv->accounts_popup), GTK_WIDGET (item)); + g_signal_connect (G_OBJECT (item), + "activate", + G_CALLBACK (on_send_receive_csm_activated), + NULL); + item = gtk_separator_menu_item_new (); + gtk_menu_shell_prepend (GTK_MENU_SHELL (priv->accounts_popup), GTK_WIDGET (item)); + } + + /* Create a new action group */ + default_account = modest_account_mgr_get_default_account (mgr); + active_account_name = modest_window_get_active_account (MODEST_WINDOW (self)); + + if (!active_account_name) + modest_window_set_active_account (MODEST_WINDOW (self), default_account); + + priv->view_additions_group = gtk_action_group_new (MODEST_MAIN_WINDOW_ACTION_GROUP_ADDITIONS); + radio_group = NULL; + for (i = 0; i < num_accounts; i++) { + gchar *display_name = NULL; + const gchar *account_name; + ModestAccountSettings *settings = (ModestAccountSettings *) g_slist_nth_data (accounts, i); + + if (!settings) { + g_warning ("%s: BUG: account_data == NULL", __FUNCTION__); + continue; + } + account_name = modest_account_settings_get_account_name (settings); + + if (default_account && account_name && + !(strcmp (default_account, account_name) == 0)) { + display_name = g_strdup_printf (_("mcen_me_toolbar_sendreceive_default"), + modest_account_settings_get_display_name (settings)); + } else { + display_name = g_strdup_printf (_("mcen_me_toolbar_sendreceive_mailbox_n"), + modest_account_settings_get_display_name (settings)); + } + + + + /* Create action and add it to the action group. The + action name must be the account name, this way we + could know in the handlers the account to show */ + if (settings && account_name) { + gchar* item_name, *refresh_action_name; + guint8 merge_id = 0; + GtkAction *view_account_action, *refresh_account_action; + gchar *escaped_display_name; + + escaped_display_name = modest_text_utils_escape_mnemonics (display_name); + + view_account_action = GTK_ACTION (gtk_radio_action_new (account_name, + escaped_display_name, NULL, NULL, 0)); + g_free (escaped_display_name); + gtk_action_group_add_action (priv->view_additions_group, view_account_action); + gtk_radio_action_set_group (GTK_RADIO_ACTION (view_account_action), radio_group); + radio_group = gtk_radio_action_get_group (GTK_RADIO_ACTION (view_account_action)); + + if (active_account_name) { + if (active_account_name && account_name && + (strcmp (active_account_name, account_name) == 0)) { + gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (view_account_action), TRUE); + } + } + + /* Add ui from account data. We allow 2^9-1 account + changes in a single execution because we're + downcasting the guint to a guint8 in order to use a + GByteArray. It should be enough :-) */ + item_name = g_strconcat (account_name, "Menu", NULL); + merge_id = (guint8) gtk_ui_manager_new_merge_id (parent_priv->ui_manager); + priv->merge_ids = g_byte_array_append (priv->merge_ids, &merge_id, 1); + gtk_ui_manager_add_ui (parent_priv->ui_manager, + merge_id, + "/MenuBar/ViewMenu/ViewMenuAdditions", + item_name, + account_name, + GTK_UI_MANAGER_MENUITEM, + FALSE); + + /* Connect the action signal "activate" */ + g_signal_connect_after (G_OBJECT (view_account_action), + "toggled", + G_CALLBACK (on_show_account_action_toggled), + self); + + /* Create the items for the Tools->Send&Receive submenu */ + refresh_action_name = g_strconcat ("SendReceive", account_name, NULL); + refresh_account_action = gtk_action_new ((const gchar*) refresh_action_name, + display_name, NULL, NULL); + gtk_action_group_add_action (priv->view_additions_group, refresh_account_action); + + merge_id = (guint8) gtk_ui_manager_new_merge_id (parent_priv->ui_manager); + priv->merge_ids = g_byte_array_append (priv->merge_ids, &merge_id, 1); + gtk_ui_manager_add_ui (parent_priv->ui_manager, + merge_id, + "/MenuBar/ToolsMenu/ToolsSendReceiveMainMenu/ToolsMenuAdditions", + item_name, + refresh_action_name, + GTK_UI_MANAGER_MENUITEM, + FALSE); + g_free (refresh_action_name); + + g_signal_connect_data (G_OBJECT (refresh_account_action), + "activate", + G_CALLBACK (on_refresh_account_action_activated), + g_strdup (account_name), + (GClosureNotify) g_free, + 0); + + /* Create item and add it to the send&receive + CSM. If there is only one account then + it'll be no menu */ + if (num_accounts > 1) { + GtkWidget *label = gtk_label_new(NULL); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); + if (default_account && (strcmp(account_name, default_account) == 0)) { + gchar *escaped = g_markup_printf_escaped ("%s", display_name); + gtk_label_set_markup (GTK_LABEL (label), escaped); + g_free (escaped); + } else { + gtk_label_set_text (GTK_LABEL (label), display_name); + } + + item = gtk_menu_item_new (); + gtk_container_add (GTK_CONTAINER (item), label); + + gtk_menu_shell_prepend (GTK_MENU_SHELL (priv->accounts_popup), + GTK_WIDGET (item)); + g_signal_connect_data (G_OBJECT (item), + "activate", + G_CALLBACK (on_send_receive_csm_activated), + g_strdup (account_name), + (GClosureNotify) g_free, + 0); + } + g_free (item_name); + } + + /* Frees */ + g_free (display_name); + } + + gtk_ui_manager_insert_action_group (parent_priv->ui_manager, priv->view_additions_group, 1); + + /* We cannot do this in the loop above because this relies on the action + * group being inserted. This makes the default account appear in bold. + * I agree it is a rather ugly way, but I don't see another possibility. armin. */ + for (i = 0; i < num_accounts; i++) { + gchar *item_name, *path; + GtkWidget *item; + ModestAccountSettings *settings; + const gchar *account_name; + gboolean is_default; + + settings = (ModestAccountSettings *) g_slist_nth_data (accounts, i); + account_name = modest_account_settings_get_account_name (settings); + is_default = (account_name && default_account && !strcmp (account_name, default_account)); + + /* Get the item of the view menu */ + item_name = g_strconcat (account_name, "Menu", NULL); + path = g_strconcat ("/MenuBar/ViewMenu/ViewMenuAdditions/", item_name, NULL); + item = gtk_ui_manager_get_widget (parent_priv->ui_manager, path); + g_free(path); + + if (item) { + GtkWidget *child = gtk_bin_get_child (GTK_BIN (item)); + if (GTK_IS_LABEL (child)) { + const gchar *cur_name = gtk_label_get_text (GTK_LABEL (child)); + if (is_default) { + gchar *bold_name = g_markup_printf_escaped("%s", cur_name); + gtk_label_set_markup (GTK_LABEL (child), bold_name); + g_free (bold_name); + } + gtk_label_set_ellipsize (GTK_LABEL (child), PANGO_ELLIPSIZE_END); + } + } + + /* Get the item of the tools menu */ + path = g_strconcat("/MenuBar/ToolsMenu/ToolsSendReceiveMainMenu/ToolsMenuAdditions/", item_name, NULL); + item = gtk_ui_manager_get_widget (parent_priv->ui_manager, path); + g_free (path); + + if (item) { + GtkWidget *child = gtk_bin_get_child (GTK_BIN (item)); + if (GTK_IS_LABEL (child)) { + const gchar *cur_name = gtk_label_get_text (GTK_LABEL (child)); + if (is_default) { + gchar *bold_name = g_markup_printf_escaped("%s", cur_name); + gtk_label_set_markup (GTK_LABEL (child), bold_name); + g_free (bold_name); + } + gtk_label_set_ellipsize (GTK_LABEL (child), PANGO_ELLIPSIZE_END); + } + } + + g_free(item_name); + g_object_unref (settings); + } + + if (num_accounts > 1) { + /* Disconnect the tap-and-hold-query if it's connected */ + if (modest_signal_mgr_is_connected (priv->sighandlers, + G_OBJECT (send_receive_button), + "tap-and-hold-query")) + priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers, + G_OBJECT (send_receive_button), + "tap-and-hold-query"); + + /* Mandatory in order to view the menu contents */ + gtk_widget_show_all (priv->accounts_popup); + + /* Setup tap_and_hold just if was not done before*/ + if (!gtk_menu_get_attach_widget (GTK_MENU (priv->accounts_popup))) + gtk_widget_tap_and_hold_setup (send_receive_button, priv->accounts_popup, NULL, 0); + } else { + /* Connect the tap-and-hold-query in order not to show the CSM */ + if (!modest_signal_mgr_is_connected (priv->sighandlers, + G_OBJECT (send_receive_button), + "tap-and-hold-query")) + priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers, + G_OBJECT (send_receive_button), + "tap-and-hold-query", + G_CALLBACK (tap_and_hold_query_cb), + NULL); + } + + /* Frees */ + g_slist_free (accounts); + g_free (default_account); + + + /* Make sure that at least one account is viewed if there are any + * accounts, for instance when adding the first account: */ + set_at_least_one_account_visible (self); +} + +static void +wrap_in_scrolled_window (GtkWidget *win, GtkWidget *widget) +{ + if (!gtk_widget_set_scroll_adjustments (widget, NULL, NULL)) + gtk_scrolled_window_add_with_viewport + (GTK_SCROLLED_WINDOW(win), widget); + else + gtk_container_add (GTK_CONTAINER(win), + widget); +} + + +typedef struct { + TnySendQueue *queue; + guint signal; +} QueueErrorSignal; + +static void +modest_main_window_cleanup_queue_error_signals (ModestMainWindow *self) +{ + ModestMainWindowPrivate *priv = MODEST_MAIN_WINDOW_GET_PRIVATE (self); + + GList *oerrsignals = priv->queue_err_signals; + while (oerrsignals) { + QueueErrorSignal *esignal = (QueueErrorSignal *) oerrsignals->data; + g_signal_handler_disconnect (esignal->queue, esignal->signal); + g_slice_free (QueueErrorSignal, esignal); + oerrsignals = g_list_next (oerrsignals); + } + g_list_free (priv->queue_err_signals); + priv->queue_err_signals = NULL; +} + + +static void +_folder_view_csm_menu_activated (GtkWidget *widget, gpointer user_data) +{ + g_return_if_fail (MODEST_IS_MAIN_WINDOW (user_data)); + + /* Update dimmed */ + modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW(user_data)); +} + +static void +_header_view_csm_menu_activated (GtkWidget *widget, gpointer user_data) +{ + g_return_if_fail (MODEST_IS_MAIN_WINDOW (user_data)); + + /* Update visibility */ + + /* Update dimmed */ + modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW(user_data)); +} + +static void +modest_main_window_disconnect_signals (ModestWindow *self) +{ + ModestMainWindowPrivate *priv; + priv = MODEST_MAIN_WINDOW_GET_PRIVATE(self); + + modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers); + priv->sighandlers = NULL; +} + +static void +connect_signals (ModestMainWindow *self) +{ + ModestWindowPrivate *parent_priv; + ModestMainWindowPrivate *priv; + GtkWidget *menu; + + priv = MODEST_MAIN_WINDOW_GET_PRIVATE(self); + parent_priv = MODEST_WINDOW_GET_PRIVATE(self); + + /* folder view */ + + priv->sighandlers = + modest_signal_mgr_connect (priv->sighandlers, + G_OBJECT(priv->folder_view), "key-press-event", + G_CALLBACK(on_inner_widgets_key_pressed), self); + priv->sighandlers = + modest_signal_mgr_connect (priv->sighandlers, G_OBJECT(priv->folder_view), + "folder_selection_changed", + G_CALLBACK (on_folder_selection_changed), + self); + priv->sighandlers = + modest_signal_mgr_connect (priv->sighandlers,G_OBJECT(priv->folder_view), + "folder-display-name-changed", + G_CALLBACK (modest_ui_actions_on_folder_display_name_changed), + self); + priv->sighandlers = + modest_signal_mgr_connect (priv->sighandlers,G_OBJECT (priv->folder_view), + "focus-in-event", + G_CALLBACK (on_folder_view_focus_in), + self); + + /* Folder view CSM */ + menu = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/FolderViewCSM"); + gtk_widget_tap_and_hold_setup (GTK_WIDGET (priv->folder_view), menu, NULL, 0); + priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers, G_OBJECT(priv->folder_view), "tap-and-hold", + G_CALLBACK(_folder_view_csm_menu_activated), + self); + /* header view */ + priv->sighandlers = + modest_signal_mgr_connect (priv->sighandlers,G_OBJECT(priv->header_view), "header_selected", + G_CALLBACK(modest_ui_actions_on_header_selected), self); + priv->sighandlers = + modest_signal_mgr_connect (priv->sighandlers,G_OBJECT(priv->header_view), "header_activated", + G_CALLBACK(modest_ui_actions_on_header_activated), self); + priv->sighandlers = + modest_signal_mgr_connect (priv->sighandlers,G_OBJECT(priv->header_view), "item_not_found", + G_CALLBACK(modest_ui_actions_on_item_not_found), self); + priv->sighandlers = + modest_signal_mgr_connect (priv->sighandlers,G_OBJECT(priv->header_view), "key-press-event", + G_CALLBACK(on_inner_widgets_key_pressed), self); + priv->sighandlers = + modest_signal_mgr_connect (priv->sighandlers,G_OBJECT(priv->header_view), "msg_count_changed", + G_CALLBACK(on_msg_count_changed), self); + priv->sighandlers = + modest_signal_mgr_connect (priv->sighandlers,G_OBJECT (priv->header_view), "focus-in-event", + G_CALLBACK (on_header_view_focus_in), self); + priv->sighandlers = + modest_signal_mgr_connect (priv->sighandlers, + G_OBJECT (priv->header_view), + "updating-msg-list", + G_CALLBACK (on_updating_msg_list), + self); + + /* Header view CSM */ + menu = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/HeaderViewCSM"); + gtk_widget_tap_and_hold_setup (GTK_WIDGET (priv->header_view), menu, NULL, 0); + priv->sighandlers = + modest_signal_mgr_connect (priv->sighandlers,G_OBJECT(priv->header_view), "tap-and-hold", + G_CALLBACK(_header_view_csm_menu_activated), + self); + + /* window */ + priv->sighandlers = + modest_signal_mgr_connect (priv->sighandlers,G_OBJECT (self), "window-state-event", + G_CALLBACK (modest_main_window_window_state_event), + NULL); + /* we don't register this in sighandlers, as it should be run after disconnecting all signals, + * in destroy stage */ + g_signal_connect (G_OBJECT (self), "destroy", G_CALLBACK (on_window_destroy), NULL); + + g_signal_connect (G_OBJECT (self), "notify::visible", G_CALLBACK (on_window_hide), NULL); + + /* Mail Operation Queue */ + priv->sighandlers = + modest_signal_mgr_connect (priv->sighandlers, + G_OBJECT (modest_runtime_get_mail_operation_queue ()), + "queue-changed", + G_CALLBACK (on_queue_changed), self); + + /* Track changes in the device name */ + priv->sighandlers = + modest_signal_mgr_connect (priv->sighandlers, + G_OBJECT(modest_runtime_get_conf ()), + "key_changed", + G_CALLBACK (on_configuration_key_changed), + self); + + /* Track account changes. We need to refresh the toolbar */ + priv->sighandlers = + modest_signal_mgr_connect (priv->sighandlers, + G_OBJECT (modest_runtime_get_account_store ()), + "account_inserted", + G_CALLBACK (on_account_inserted), + self); + priv->sighandlers = + modest_signal_mgr_connect (priv->sighandlers, + G_OBJECT (modest_runtime_get_account_store ()), + "account_removed", + G_CALLBACK (on_account_removed), + self); + + /* We need to refresh the send & receive menu to change the bold + * account when the default account changes. */ + priv->sighandlers = + modest_signal_mgr_connect (priv->sighandlers, + G_OBJECT (modest_runtime_get_account_mgr ()), + "default_account_changed", + G_CALLBACK (on_default_account_changed), + self); + + /* Account store */ + priv->sighandlers = + modest_signal_mgr_connect (priv->sighandlers, + G_OBJECT (modest_runtime_get_account_store ()), + "account_changed", + G_CALLBACK (on_account_changed), + self); +} + +static void +on_hildon_program_is_topmost_notify(GObject *self, + GParamSpec *propert_param, + gpointer user_data) +{ + HildonProgram *app = HILDON_PROGRAM (self); + + /* Note that use of hildon_program_set_can_hibernate() + * is generally referred to as "setting the killable flag", + * though hibernation does not seem equal to death. + * murrayc */ + + if (hildon_program_get_is_topmost (app)) { + /* Prevent hibernation when the progam comes to the foreground, + * because hibernation should only happen when the application + * is in the background: */ + hildon_program_set_can_hibernate (app, FALSE); + + /* Remove new mail visual notifications */ + modest_platform_remove_new_mail_notifications (TRUE); + } else { + /* Allow hibernation if the program has gone to the background: */ + + /* However, prevent hibernation while the settings are being changed: */ + const gboolean hibernation_prevented = + modest_window_mgr_get_hibernation_is_prevented ( + modest_runtime_get_window_mgr ()); + + if (hibernation_prevented) + hildon_program_set_can_hibernate (app, FALSE); + else { + /* Allow hibernation, after saving the state: */ + modest_osso_save_state(); + hildon_program_set_can_hibernate (app, TRUE); + } + } +} + +typedef struct +{ + GtkWidget *folder_win; + gulong handler_id; +} ShowHelper; + +static void +modest_main_window_on_show (GtkWidget *self, gpointer user_data) +{ + ShowHelper *helper = (ShowHelper *) user_data; + GtkWidget *folder_win = helper->folder_win; + ModestMainWindowPrivate *priv = MODEST_MAIN_WINDOW_GET_PRIVATE(self); + + priv->folder_view = MODEST_FOLDER_VIEW (modest_platform_create_folder_view (NULL)); + wrap_in_scrolled_window (folder_win, GTK_WIDGET(priv->folder_view)); + + gtk_widget_show (GTK_WIDGET (priv->folder_view)); + + /* Connect signals */ + connect_signals (MODEST_MAIN_WINDOW (self)); + + /* Set account store */ + tny_account_store_view_set_account_store (TNY_ACCOUNT_STORE_VIEW (priv->folder_view), + TNY_ACCOUNT_STORE (modest_runtime_get_account_store ())); + + /* Load previous osso state, for instance if we are being restored from + * hibernation: */ + modest_osso_load_state (); + + /* Restore window & widget settings */ + priv->wait_for_settings = TRUE; + restore_settings (MODEST_MAIN_WINDOW(self), TRUE); + priv->wait_for_settings = FALSE; + + /* Check if accounts exist and show the account wizard if not */ + gboolean accounts_exist = + modest_account_mgr_has_accounts(modest_runtime_get_account_mgr(), TRUE); + + if (!accounts_exist) { + /* This is necessary to have the main window shown behind the dialog + It's an ugly hack... jschmid */ + gtk_widget_show_all(GTK_WIDGET(self)); + modest_ui_actions_on_accounts (NULL, MODEST_WINDOW(self)); + } else { + update_menus (MODEST_MAIN_WINDOW (self)); + } + + /* Never call this function again (NOTE that it could happen + as we hide the main window instead of closing it while + there are operations ongoing) and free the helper */ + g_signal_handler_disconnect (self, helper->handler_id); + g_slice_free (ShowHelper, helper); +} + +static void +osso_display_event_cb (osso_display_state_t state, + gpointer data) +{ + ModestMainWindowPrivate *priv = MODEST_MAIN_WINDOW_GET_PRIVATE (data); + + priv->display_state = state; + + /* Stop blinking if the screen becomes on */ + if (priv->display_state == OSSO_DISPLAY_ON) + modest_platform_remove_new_mail_notifications (TRUE); +} + +ModestWindow * +modest_main_window_new (void) +{ + ModestMainWindow *self = NULL; + ModestMainWindowPrivate *priv = NULL; + ModestWindowPrivate *parent_priv = NULL; + GtkWidget *folder_win = NULL; + ModestDimmingRulesGroup *menu_rules_group = NULL; + ModestDimmingRulesGroup *toolbar_rules_group = NULL; + GtkActionGroup *action_group = NULL; + GError *error = NULL; + HildonProgram *app; + ModestConf *conf = NULL; + GtkAction *action = NULL; + GdkPixbuf *window_icon; + ShowHelper *helper; + + self = MODEST_MAIN_WINDOW(g_object_new(MODEST_TYPE_MAIN_WINDOW, NULL)); + priv = MODEST_MAIN_WINDOW_GET_PRIVATE(self); + parent_priv = MODEST_WINDOW_GET_PRIVATE(self); + + parent_priv->ui_manager = gtk_ui_manager_new(); + parent_priv->ui_dimming_manager = modest_ui_dimming_manager_new(); + + action_group = gtk_action_group_new ("ModestMainWindowActions"); + gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE); + + menu_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_MENU, FALSE); + toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE); + + /* Add common actions */ + gtk_action_group_add_actions (action_group, + modest_action_entries, + G_N_ELEMENTS (modest_action_entries), + self); + + gtk_action_group_add_actions (action_group, + modest_folder_view_action_entries, + G_N_ELEMENTS (modest_folder_view_action_entries), + self); + + gtk_action_group_add_actions (action_group, + modest_header_view_action_entries, + G_N_ELEMENTS (modest_header_view_action_entries), + self); + + gtk_action_group_add_toggle_actions (action_group, + modest_toggle_action_entries, + G_N_ELEMENTS (modest_toggle_action_entries), + self); + + gtk_action_group_add_toggle_actions (action_group, + modest_main_window_toggle_action_entries, + G_N_ELEMENTS (modest_main_window_toggle_action_entries), + self); + + gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0); + g_object_unref (action_group); + + /* Load the UI definition */ + gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, + MODEST_UIDIR "modest-main-window-ui.xml", &error); + if (error != NULL) { + g_warning ("Could not merge modest-ui.xml: %s", error->message); + g_error_free (error); + error = NULL; + } + + /* Add common dimming rules */ + modest_dimming_rules_group_add_rules (menu_rules_group, + modest_main_window_menu_dimming_entries, + G_N_ELEMENTS (modest_main_window_menu_dimming_entries), + MODEST_WINDOW (self)); + modest_dimming_rules_group_add_rules (toolbar_rules_group, + modest_main_window_toolbar_dimming_entries, + G_N_ELEMENTS (modest_main_window_toolbar_dimming_entries), + MODEST_WINDOW (self)); + + /* Insert dimming rules group for this window */ + modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, menu_rules_group); + modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group); + g_object_unref (menu_rules_group); + g_object_unref (toolbar_rules_group); + + /* Add accelerators */ + gtk_window_add_accel_group (GTK_WINDOW (self), + gtk_ui_manager_get_accel_group (parent_priv->ui_manager)); + + /* Menubar. Update the state of some toggles */ + parent_priv->menubar = modest_maemo_utils_get_manager_menubar_as_menu (parent_priv->ui_manager, "/MenuBar"); + conf = modest_runtime_get_conf (); + action = gtk_ui_manager_get_action (parent_priv->ui_manager, + "/MenuBar/ViewMenu/ViewShowToolbarMainMenu/ViewShowToolbarNormalScreenMenu"); + gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), + modest_conf_get_bool (conf, MODEST_CONF_MAIN_WINDOW_SHOW_TOOLBAR, NULL)); + action = gtk_ui_manager_get_action (parent_priv->ui_manager, + "/MenuBar/ViewMenu/ViewShowToolbarMainMenu/ViewShowToolbarFullScreenMenu"); + gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), + modest_conf_get_bool (conf, MODEST_CONF_MAIN_WINDOW_SHOW_TOOLBAR_FULLSCREEN, NULL)); + hildon_window_set_menu (HILDON_WINDOW (self), GTK_MENU (parent_priv->menubar)); + gtk_widget_show (parent_priv->menubar); + + /* Get device name */ + modest_maemo_utils_get_device_name (); + + /* header view */ + priv->header_view = + MODEST_HEADER_VIEW (modest_header_view_new (NULL, MODEST_HEADER_VIEW_STYLE_DETAILS)); + g_object_ref (priv->header_view); + if (!priv->header_view) + g_printerr ("modest: cannot instantiate header view\n"); + modest_header_view_set_style (priv->header_view, MODEST_HEADER_VIEW_STYLE_TWOLINES); + modest_widget_memory_restore (modest_runtime_get_conf (), G_OBJECT(priv->header_view), + MODEST_CONF_HEADER_VIEW_KEY); + + /* Other style properties of header view */ + g_object_set (G_OBJECT (priv->header_view), + "rules-hint", FALSE, + NULL); + /* gtk_widget_show (priv->header_view); */ + + /* Empty view */ + priv->empty_view = create_empty_view (); + gtk_widget_show (priv->empty_view); + g_object_ref (priv->empty_view); + + /* Create scrolled windows */ + folder_win = gtk_scrolled_window_new (NULL, NULL); + priv->contents_widget = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (folder_win), + GTK_POLICY_NEVER, + GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->contents_widget), + GTK_POLICY_NEVER, + GTK_POLICY_AUTOMATIC); + /* gtk_widget_show (priv->contents_widget); */ + + /* paned */ + priv->main_paned = gtk_hpaned_new (); + gtk_paned_pack1 (GTK_PANED(priv->main_paned), folder_win, TRUE, TRUE); + gtk_paned_pack2 (GTK_PANED(priv->main_paned), priv->contents_widget, TRUE, TRUE); + gtk_tree_view_columns_autosize (GTK_TREE_VIEW(priv->header_view)); + + /* putting it all together... */ + priv->main_vbox = gtk_vbox_new (FALSE, 6); + gtk_box_pack_start (GTK_BOX(priv->main_vbox), priv->main_paned, TRUE, TRUE,0); + gtk_widget_show (priv->main_vbox); + + gtk_container_add (GTK_CONTAINER(self), priv->main_vbox); + + app = hildon_program_get_instance (); + hildon_program_add_window (app, HILDON_WINDOW (self)); + + g_signal_connect (G_OBJECT(app), "notify::is-topmost", + G_CALLBACK (on_hildon_program_is_topmost_notify), self); + + /* Connect to "show" action. We delay the creation of some + elements until that moment */ + helper = g_slice_new0 (ShowHelper); + helper->folder_win = folder_win; + helper->handler_id = g_signal_connect (G_OBJECT(self), "show", + G_CALLBACK (modest_main_window_on_show), + helper); + + /* Set window icon */ + window_icon = modest_platform_get_icon (MODEST_APP_ICON, MODEST_ICON_SIZE_BIG); + if (window_icon) { + gtk_window_set_icon (GTK_WINDOW (self), window_icon); + g_object_unref (window_icon); + } + + /* Listen for changes in the screen, we don't want to show a + led pattern when the display is on for example */ + osso_hw_set_display_event_cb (modest_maemo_utils_get_osso_context (), + osso_display_event_cb, + self); + + /* Dont't restore settings here, + * because it requires a gtk_widget_show(), + * and we don't want to do that until later, + * so that the UI is not visible for non-menu D-Bus activation. + */ + + return MODEST_WINDOW(self); +} + +void +modest_main_window_set_style (ModestMainWindow *self, + ModestMainWindowStyle style) +{ + ModestMainWindowPrivate *priv; + ModestWindowPrivate *parent_priv; + GtkAction *action; + gboolean active; + GtkTreeSelection *sel; + GList *rows, *list; + + g_return_if_fail (MODEST_IS_MAIN_WINDOW (self)); + + priv = MODEST_MAIN_WINDOW_GET_PRIVATE(self); + parent_priv = MODEST_WINDOW_GET_PRIVATE(self); + + /* no change -> nothing to do */ + if (priv->style == style) + return; + + /* Get toggle button and update the state if needed. This will + happen only when the set_style is not invoked from the UI, + for example when it's called from widget memory */ + action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToggleFolders"); + active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)); + if ((active && style == MODEST_MAIN_WINDOW_STYLE_SIMPLE) || + (!active && style == MODEST_MAIN_WINDOW_STYLE_SPLIT)) { + g_signal_handlers_block_by_func (action, modest_ui_actions_toggle_folders_view, self); + gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), !active); + g_signal_handlers_unblock_by_func (action, modest_ui_actions_toggle_folders_view, self); + } + + /* We need to store the selection because it's lost when the + widget is reparented */ + sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->header_view)); + rows = gtk_tree_selection_get_selected_rows (sel, NULL); + + priv->style = style; + switch (style) { + case MODEST_MAIN_WINDOW_STYLE_SIMPLE: + + if (!priv->wait_for_settings) + modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT (priv->main_paned), + MODEST_CONF_MAIN_PANED_KEY); + /* Remove main paned */ + g_object_ref (priv->main_paned); + gtk_container_remove (GTK_CONTAINER (priv->main_vbox), priv->main_paned); + + /* Reparent the contents widget to the main vbox */ + gtk_widget_reparent (priv->contents_widget, priv->main_vbox); + + break; + case MODEST_MAIN_WINDOW_STYLE_SPLIT: + /* Remove header view */ + g_object_ref (priv->contents_widget); + gtk_container_remove (GTK_CONTAINER (priv->main_vbox), priv->contents_widget); + + /* Reparent the main paned */ + gtk_paned_add2 (GTK_PANED (priv->main_paned), priv->contents_widget); + gtk_container_add (GTK_CONTAINER (priv->main_vbox), priv->main_paned); + + g_timeout_add (500, (GSourceFunc) restore_paned_timeout_handler, self); + + break; + default: + g_list_foreach (rows, (GFunc) gtk_tree_path_free, NULL); + g_list_free (rows); + g_return_if_reached (); + } + + /* Reselect the previously selected folders. We disable the + dimming rules execution during that time because there is + no need to work out it again and it could take a lot of + time if all the headers are selected */ + list = rows; + modest_window_disable_dimming (MODEST_WINDOW (self)); + while (list) { + gtk_tree_selection_select_path (sel, (GtkTreePath *) list->data); + list = g_list_next (list); + } + modest_window_enable_dimming (MODEST_WINDOW (self)); + + /* Free */ + g_list_foreach (rows, (GFunc) gtk_tree_path_free, NULL); + g_list_free (rows); + + /* Let header view grab the focus if it's being shown */ + if (priv->contents_style == MODEST_MAIN_WINDOW_CONTENTS_STYLE_HEADERS) { + gtk_widget_grab_focus (GTK_WIDGET (priv->header_view)); + } else { + if (priv->style == MODEST_MAIN_WINDOW_STYLE_SPLIT) + gtk_widget_grab_focus (GTK_WIDGET (priv->folder_view)); + else + gtk_widget_grab_focus (GTK_WIDGET (priv->contents_widget)); + } + + /* Check dimming rules */ + modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self)); + modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (self)); + + /* Show changes */ + gtk_widget_show_all (GTK_WIDGET (priv->main_vbox)); +} + +ModestMainWindowStyle +modest_main_window_get_style (ModestMainWindow *self) +{ + ModestMainWindowPrivate *priv; + + g_return_val_if_fail (MODEST_IS_MAIN_WINDOW (self), -1); + + priv = MODEST_MAIN_WINDOW_GET_PRIVATE(self); + return priv->style; +} + +static void +toolbar_resize (ModestMainWindow *self) +{ + ModestMainWindowPrivate *priv = NULL; + ModestWindowPrivate *parent_priv = NULL; + GtkWidget *widget; + gint static_button_size; + ModestWindowMgr *mgr; + + g_return_if_fail (MODEST_IS_MAIN_WINDOW (self)); + priv = MODEST_MAIN_WINDOW_GET_PRIVATE(self); + parent_priv = MODEST_WINDOW_GET_PRIVATE(self); + + mgr = modest_runtime_get_window_mgr (); + static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?118:108; + + if (parent_priv->toolbar) { + /* left size buttons */ + widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNew"); + gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE); + gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE); + gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1); + widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply"); + gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE); + gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE); + gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1); + widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage"); + gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE); + gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE); + gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1); + widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToggleFolders"); + gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE); + gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE); + gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1); + + gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE); + gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE); + gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE); + gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE); + gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->refresh_toolitem), TRUE); + gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->refresh_toolitem), TRUE); + gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->sort_toolitem), TRUE); + gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->sort_toolitem), TRUE); + } + +} + + + +static gboolean +modest_main_window_window_state_event (GtkWidget *widget, GdkEventWindowState *event, gpointer userdata) +{ + if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) { + ModestWindowPrivate *parent_priv; + ModestWindowMgr *mgr; + gboolean is_fullscreen; + GtkAction *fs_toggle_action; + gboolean active; + + mgr = modest_runtime_get_window_mgr (); + + is_fullscreen = modest_window_mgr_get_fullscreen_mode (mgr); + + parent_priv = MODEST_WINDOW_GET_PRIVATE (widget); + + fs_toggle_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewToggleFullscreenMenu"); + active = (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (fs_toggle_action)))?1:0; + if (is_fullscreen != active) { + gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (fs_toggle_action), is_fullscreen); + } + + toolbar_resize (MODEST_MAIN_WINDOW (widget)); + } + + return FALSE; + +} + +static void +modest_main_window_show_toolbar (ModestWindow *self, + gboolean show_toolbar) +{ + ModestMainWindowPrivate *priv = NULL; + ModestWindowPrivate *parent_priv = NULL; + GtkWidget *reply_button = NULL, *menu = NULL; + GtkWidget *placeholder = NULL; + gint insert_index; + const gchar *action_name; + GtkAction *action; + + g_return_if_fail (MODEST_IS_MAIN_WINDOW (self)); + priv = MODEST_MAIN_WINDOW_GET_PRIVATE(self); + parent_priv = MODEST_WINDOW_GET_PRIVATE(self); + + /* Set optimized view status */ + priv->optimized_view = !show_toolbar; + + if (!parent_priv->toolbar) { + parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager, + "/ToolBar"); + gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE); + + priv->progress_toolitem = GTK_WIDGET (gtk_tool_item_new ()); + priv->cancel_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarCancel"); + priv->refresh_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarSendReceive"); + priv->sort_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarSort"); + toolbar_resize (MODEST_MAIN_WINDOW (self)); + + /* Add ProgressBar (Transfer toolbar) */ + priv->progress_bar = modest_progress_bar_new (); + gtk_widget_set_no_show_all (priv->progress_bar, TRUE); + placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ProgressBarView"); + insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder)); + gtk_container_add (GTK_CONTAINER (priv->progress_toolitem), priv->progress_bar); + gtk_toolbar_insert(GTK_TOOLBAR(parent_priv->toolbar), GTK_TOOL_ITEM (priv->progress_toolitem), insert_index); + + /* Connect cancel 'clicked' signal to abort progress mode */ + g_signal_connect(priv->cancel_toolitem, "clicked", + G_CALLBACK(cancel_progressbar), + self); + + /* Add it to the observers list */ + priv->progress_widgets = g_slist_prepend(priv->progress_widgets, priv->progress_bar); + + /* Add to window */ + hildon_window_add_toolbar (HILDON_WINDOW (self), + GTK_TOOLBAR (parent_priv->toolbar)); + + /* Set reply button tap and hold menu */ + reply_button = gtk_ui_manager_get_widget (parent_priv->ui_manager, + "/ToolBar/ToolbarMessageReply"); + menu = gtk_ui_manager_get_widget (parent_priv->ui_manager, + "/ToolbarReplyCSM"); + gtk_widget_tap_and_hold_setup (GTK_WIDGET (reply_button), menu, NULL, 0); + + /* Set send & receive button tap and hold menu */ + update_menus (MODEST_MAIN_WINDOW (self)); + } + + if (show_toolbar) { + /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */ + /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */ + gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE); + + gtk_widget_show (GTK_WIDGET (parent_priv->toolbar)); + if (modest_main_window_transfer_mode_enabled (MODEST_MAIN_WINDOW(self))) + set_toolbar_mode (MODEST_MAIN_WINDOW(self), TOOLBAR_MODE_TRANSFER); + else + set_toolbar_mode (MODEST_MAIN_WINDOW(self), TOOLBAR_MODE_NORMAL); + } else { + gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar)); + + } + + /* Update also the actions (to update the toggles in the + menus), we have to do it manually because some other window + of the same time could have changed it (remember that the + toolbar fullscreen mode is shared by all the windows of the + same type */ + if (modest_window_mgr_get_fullscreen_mode (modest_runtime_get_window_mgr ())) + action_name = "/MenuBar/ViewMenu/ViewShowToolbarMainMenu/ViewShowToolbarFullScreenMenu"; + else + action_name = "/MenuBar/ViewMenu/ViewShowToolbarMainMenu/ViewShowToolbarNormalScreenMenu"; + + action = gtk_ui_manager_get_action (parent_priv->ui_manager, action_name); + modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), + show_toolbar); +} + +static void +on_account_inserted (TnyAccountStore *accoust_store, + TnyAccount *account, + gpointer user_data) +{ + /* Transport accounts and local ones (MMC and the Local + folders account do now cause menu changes */ + if (TNY_IS_STORE_ACCOUNT (account) && + modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (account))) { + /* Update menus */ + update_menus (MODEST_MAIN_WINDOW (user_data)); + } +} + +static void +on_default_account_changed (ModestAccountMgr* mgr, + gpointer user_data) +{ + update_menus (MODEST_MAIN_WINDOW (user_data)); +} + +static void +on_account_removed (TnyAccountStore *accoust_store, + TnyAccount *account, + gpointer user_data) +{ + /* Transport accounts and local ones (MMC and the Local + folders account do now cause menu changes */ + if (TNY_IS_STORE_ACCOUNT (account) && + modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (account))) + update_menus (MODEST_MAIN_WINDOW (user_data)); +} + +static void +on_account_changed (TnyAccountStore *account_store, + TnyAccount *account, + gpointer user_data) +{ + ModestMainWindow *win = MODEST_MAIN_WINDOW (user_data); + + /* Transport accounts and local ones (MMC and the Local + folders account do now cause menu changes */ + if (TNY_IS_STORE_ACCOUNT (account)) { + /* We need to refresh the details widget because it could have changed */ + if (modest_main_window_get_contents_style(win) == MODEST_MAIN_WINDOW_CONTENTS_STYLE_DETAILS) + modest_main_window_set_contents_style (win, MODEST_MAIN_WINDOW_CONTENTS_STYLE_DETAILS); + + /* Update the menus as well, name could change */ + if (modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (account))) + update_menus (MODEST_MAIN_WINDOW (user_data)); + } +} + +/* + * This function manages the key events used to navigate between + * header and folder views (when the window is in split view) + * + * FROM KEY ACTION + * ------------------------------------------------- + * HeaderView GDK_Left Move focus to folder view + * FolderView GDK_Right Move focus to header view + * + * There is no need to scroll to selected row, the widgets will be the + * responsibles of doing that (probably managing the focus-in event + */ +static gboolean +on_inner_widgets_key_pressed (GtkWidget *widget, + GdkEventKey *event, + gpointer user_data) +{ + ModestMainWindowPrivate *priv; + + if (event->type == GDK_KEY_RELEASE) + return FALSE; + + priv = MODEST_MAIN_WINDOW_GET_PRIVATE (user_data); + + /* Do nothing if we're in SIMPLE style */ + if (priv->style == MODEST_MAIN_WINDOW_STYLE_SIMPLE) + return FALSE; + + if (MODEST_IS_HEADER_VIEW (widget)) { + if (event->keyval == GDK_Left) + gtk_widget_grab_focus (GTK_WIDGET (priv->folder_view)); + else if ((event->keyval == GDK_Return)||(event->keyval == GDK_KP_Enter)) { + guint selected_headers = modest_header_view_count_selected_headers (MODEST_HEADER_VIEW (widget)); + if (selected_headers > 1) { + hildon_banner_show_information (NULL, NULL, _("mcen_ib_select_one_message")); + return TRUE; + } else { + GtkTreePath * cursor_path; + gtk_tree_view_get_cursor (GTK_TREE_VIEW (widget), &cursor_path, NULL); + if (cursor_path == NULL) { + GtkTreeSelection *selection; + GList *list; + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget)); + list = gtk_tree_selection_get_selected_rows (selection, NULL); + + if (list != NULL) + gtk_tree_view_set_cursor (GTK_TREE_VIEW (widget), (GtkTreePath *) list->data, NULL, FALSE); + g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL); + g_list_free (list); + } + } + } + } else if (MODEST_IS_FOLDER_VIEW (widget) && (event->keyval == GDK_Right || event->keyval == GDK_Left)) { +#if GTK_CHECK_VERSION(2, 8, 0) /* TODO: gtk_tree_view_get_visible_range() is only available in GTK+ 2.8 */ + GtkTreePath *selected_path = NULL; + GtkTreePath *start_path = NULL; + GtkTreePath *end_path = NULL; + GList *selected; + GtkTreeSelection *selection; + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->header_view)); + selected = gtk_tree_selection_get_selected_rows (selection, NULL); + if (selected != NULL) { + selected_path = (GtkTreePath *) selected->data; + if (gtk_tree_view_get_visible_range (GTK_TREE_VIEW (priv->header_view), + &start_path, + &end_path)) { + + if ((gtk_tree_path_compare (start_path, selected_path) != -1) || + (gtk_tree_path_compare (end_path, selected_path) != 1)) { + + /* Scroll to first path */ + gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (priv->header_view), + selected_path, + NULL, + TRUE, + 0.5, + 0.0); + } + } + if (start_path) + gtk_tree_path_free (start_path); + if (end_path) + gtk_tree_path_free (end_path); + g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL); + g_list_free (selected); + } +#endif /* GTK_CHECK_VERSION */ + /* fix scroll */ + gtk_widget_grab_focus (GTK_WIDGET (priv->header_view)); + } + + return FALSE; +} + +static void +set_alignment (GtkWidget *widget, + gpointer data) +{ + gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.0); + gtk_misc_set_padding (GTK_MISC (widget), 0, 0); +} + +static GtkWidget * +create_empty_view (void) +{ + GtkLabel *label = NULL; + GtkWidget *align = NULL; + + align = gtk_alignment_new(XALIGN, YALIGN, XSPACE, YSPACE); + label = GTK_LABEL(gtk_label_new (_("mcen_ia_nomessages"))); + gtk_label_set_justify (label, GTK_JUSTIFY_CENTER); + gtk_container_add (GTK_CONTAINER (align), GTK_WIDGET(label)); + + return GTK_WIDGET(align); +} + +/* + * Free the returned string + */ +static gchar * +get_gray_color_markup (GtkWidget *styled_widget) +{ + gchar *gray_color_markup = NULL; +#ifndef MODEST_HAVE_HILDON0_WIDGETS + /* Obtain the secondary text color. We need a realized widget, that's why + we get styled_widget from outside */ + GdkColor color; + if (gtk_style_lookup_color (styled_widget->style, "SecondaryTextColor", &color)) + gray_color_markup = modest_text_utils_get_color_string (&color); +#endif /*MODEST_HAVE_HILDON0_WIDGETS*/ + + if (!gray_color_markup) + gray_color_markup = g_strdup ("#BBBBBB"); + + return gray_color_markup; +} + +/* + * Free the returned string + */ +static gchar* +create_device_name_visual_string (const gchar *device_name, + const gchar *gray_color_markup) +{ + gchar *tmp, *label; + + /* We have to use "" to fill the %s of the translation. We can + not just use the device name because the device name is + shown in a different color, so it could not be included + into the tag */ + tmp = g_strdup_printf (_("mcen_fi_localroot_description"), ""); + label = g_markup_printf_escaped ("%s%s", + gray_color_markup, + tmp, + device_name); + g_free (tmp); + + return label; +} + +typedef struct +{ + GtkWidget *count_label; + GtkWidget *msg_count_label; + GtkWidget *size_label; + gchar *color_markup; +} DetailsWidgets; + +static gchar * +create_uint_label (const gchar *markup, + const gchar *name, + guint count) +{ + return g_markup_printf_escaped ("%s: %d", markup, name, count); +} + +static gchar * +create_gchar_label (const gchar *markup, + const gchar *name, + gchar *count) +{ + return g_markup_printf_escaped ("%s: %s", markup, name, count); +} + +static void +update_folder_stats_status_cb (ModestFolderStats stats, + gpointer user_data) +{ + DetailsWidgets *widgets = (DetailsWidgets *) user_data; + gchar *label, *tmp; + + label = create_uint_label (widgets->color_markup, _("mcen_fi_rootfolder_folders"), stats.folders); + gtk_label_set_markup (GTK_LABEL (widgets->count_label), label); + g_free (label); + + label = create_uint_label (widgets->color_markup, _("mcen_fi_rootfolder_messages"), stats.msg_count); + gtk_label_set_markup (GTK_LABEL (widgets->msg_count_label), label); + g_free (label); + + if (widgets->size_label) { + tmp = modest_text_utils_get_display_size (stats.local_size); + label = create_gchar_label (widgets->color_markup, _("mcen_fi_rootfolder_size"), tmp); + gtk_label_set_markup (GTK_LABEL (widgets->size_label), label); + g_free (label); + g_free (tmp); + } +} + +static void +update_folder_stats_cb (ModestFolderStats stats, + gpointer user_data) +{ + DetailsWidgets *widgets = (DetailsWidgets *) user_data; + + /* refresh data */ + update_folder_stats_status_cb (stats, user_data); + + /* frees. Note that the widgets could have been destroyed but + we still keep a reference */ + g_free (widgets->color_markup); + if (widgets->count_label) + g_object_unref (widgets->count_label); + if (widgets->msg_count_label) + g_object_unref (widgets->msg_count_label); + if (widgets->size_label) + g_object_unref (widgets->size_label); + g_slice_free (DetailsWidgets, widgets); +} + +static GtkWidget * +create_details_widget (GtkWidget *styled_widget, TnyAccount *account) +{ + /* TODO: Clean up this function. It's a mess, with lots of copy/paste. murrayc. */ + + GtkWidget *vbox; + GtkWidget *label_w; + gchar *label; + gchar *gray_color_markup; + DetailsWidgets *widgets; + + vbox = gtk_vbox_new (FALSE, 0); + widgets = g_slice_new0 (DetailsWidgets); + + gray_color_markup = get_gray_color_markup (styled_widget); + widgets->color_markup = g_strdup (gray_color_markup); + + /* Account description: */ + if (modest_tny_account_is_virtual_local_folders (account) + || (modest_tny_account_is_memory_card_account (account))) { + + /* Get device name */ + gchar *device_name = NULL; + if (modest_tny_account_is_virtual_local_folders (account)) + device_name = modest_conf_get_string (modest_runtime_get_conf(), + MODEST_CONF_DEVICE_NAME, NULL); + else + device_name = g_strdup (tny_account_get_name (account)); + + label = create_device_name_visual_string ((const gchar *) device_name, + (const gchar *) gray_color_markup); + label_w = gtk_label_new (NULL); + gtk_label_set_markup (GTK_LABEL (label_w), label); + gtk_label_set_ellipsize (GTK_LABEL (label_w), PANGO_ELLIPSIZE_END); + gtk_box_pack_start (GTK_BOX (vbox), label_w, FALSE, FALSE, 0); + g_free (device_name); + g_free (label); + } else { + if(!strcmp (tny_account_get_id (account), MODEST_MMC_ACCOUNT_ID)) { + gtk_box_pack_start (GTK_BOX (vbox), + gtk_label_new (tny_account_get_name (account)), + FALSE, FALSE, 0); + } else { + /* Other accounts, such as IMAP and POP: */ + + GString *proto; + gchar *tmp; + + /* Put proto in uppercase */ + proto = g_string_new (tny_account_get_proto (account)); + proto = g_string_ascii_up (proto); + + /* note: mcen_fi_localroot_description is something like "%s account" + * however, we should display "%s account: %s"... therefore, ugly tmp */ + tmp = g_strdup_printf (_("mcen_fi_remoteroot_account"),proto->str); + label = g_markup_printf_escaped ("%s: %s", + gray_color_markup, tmp, tny_account_get_name (account)); + g_free (tmp); + + label_w = gtk_label_new (NULL); + gtk_label_set_markup (GTK_LABEL (label_w), label); + gtk_label_set_ellipsize (GTK_LABEL (label_w), PANGO_ELLIPSIZE_END); + gtk_box_pack_start (GTK_BOX (vbox), label_w, FALSE, FALSE, 0); + g_string_free (proto, TRUE); + g_free (label); + } + } + + /* Message count */ + TnyFolderStore *folder_store = TNY_FOLDER_STORE (account); + label = create_uint_label (gray_color_markup, _("mcen_fi_rootfolder_messages"), 0); + label_w = gtk_label_new (NULL); + gtk_label_set_markup (GTK_LABEL (label_w), label); + gtk_label_set_ellipsize (GTK_LABEL (label_w), PANGO_ELLIPSIZE_END); + gtk_box_pack_start (GTK_BOX (vbox), label_w, FALSE, FALSE, 0); + g_free (label); + + widgets->msg_count_label = g_object_ref (label_w); + + /* Folder count */ + label = create_uint_label (gray_color_markup, _("mcen_fi_rootfolder_folders"), 0); + label_w = gtk_label_new (NULL); + gtk_label_set_markup (GTK_LABEL (label_w), label); + gtk_label_set_ellipsize (GTK_LABEL (label_w), PANGO_ELLIPSIZE_END); + gtk_box_pack_start (GTK_BOX (vbox), label_w, FALSE, FALSE, 0); + g_free (label); + + widgets->count_label = g_object_ref (label_w); + + /* Size / Date */ + if (modest_tny_account_is_virtual_local_folders (account) + || modest_tny_account_is_memory_card_account (account)) { + + label = create_gchar_label (gray_color_markup, _("mcen_fi_rootfolder_size"), "0"); + + label_w = gtk_label_new (NULL); + gtk_label_set_markup (GTK_LABEL (label_w), label); + gtk_label_set_ellipsize (GTK_LABEL (label_w), PANGO_ELLIPSIZE_END); + gtk_box_pack_start (GTK_BOX (vbox), label_w, FALSE, FALSE, 0); + g_free (label); + + widgets->size_label = g_object_ref (label_w); + + } else if (TNY_IS_ACCOUNT(folder_store)) { + TnyAccount *account = TNY_ACCOUNT(folder_store); + + time_t last_updated; + const gchar *last_updated_string; + /* Get last updated from configuration */ + last_updated = modest_account_mgr_get_last_updated (modest_runtime_get_account_mgr (), + tny_account_get_id (account)); + + if (last_updated > 0) + last_updated_string = modest_text_utils_get_display_date(last_updated); + else + last_updated_string = g_strdup (_("mcen_va_never")); + + label = g_markup_printf_escaped ("%s: %s", + gray_color_markup, _("mcen_ti_lastupdated"), last_updated_string); + label_w = gtk_label_new (NULL); + gtk_label_set_markup (GTK_LABEL (label_w), label); + gtk_label_set_ellipsize (GTK_LABEL (label_w), PANGO_ELLIPSIZE_END); + gtk_box_pack_start (GTK_BOX (vbox), label_w, FALSE, FALSE, 0); + g_free (label); + } + + g_free (gray_color_markup); + + /* Refresh folder stats asynchronously */ + modest_tny_folder_store_get_folder_stats (TNY_FOLDER_STORE (account), + update_folder_stats_cb, + update_folder_stats_status_cb, + widgets); + + /* Set alignment */ + gtk_container_foreach (GTK_CONTAINER (vbox), (GtkCallback) set_alignment, NULL); + + return vbox; +} + +gboolean +modest_main_window_send_receive_in_progress (ModestMainWindow *self) +{ + ModestMainWindowPrivate *priv = NULL; + + g_return_val_if_fail (MODEST_IS_MAIN_WINDOW (self), FALSE); + + priv = MODEST_MAIN_WINDOW_GET_PRIVATE(self); + + return priv->send_receive_in_progress; +} + +void +modest_main_window_notify_send_receive_initied (ModestMainWindow *self) +{ + GtkAction *action = NULL; + GtkWidget *widget = NULL; + ModestMainWindowPrivate *priv = NULL; + + g_return_if_fail (MODEST_IS_MAIN_WINDOW (self)); + priv = MODEST_MAIN_WINDOW_GET_PRIVATE(self); + + priv->send_receive_in_progress = TRUE; + + action = modest_window_get_action (MODEST_WINDOW(self), "/MenuBar/ToolsMenu/ToolsSendReceiveMainMenu/ToolsSendReceiveAllMenu"); + gtk_action_set_sensitive (action, FALSE); +/* action = modest_window_get_action (MODEST_WINDOW(self), "/MenuBar/ToolsMenu/ToolsSendReceiveMainMenu/ToolsSendReceiveCancelSendingMenu"); */ +/* gtk_action_set_sensitive (action, FALSE); */ + widget = modest_window_get_action_widget (MODEST_WINDOW(self), "/MenuBar/ToolsMenu/ToolsSendReceiveMainMenu/ToolsMenuAdditions"); + gtk_widget_set_sensitive (widget, FALSE); +} + +void +modest_main_window_notify_send_receive_completed (ModestMainWindow *self) +{ + GtkAction *action = NULL; + GtkWidget *widget = NULL; + ModestMainWindowPrivate *priv = NULL; + + g_return_if_fail (MODEST_IS_MAIN_WINDOW (self)); + priv = MODEST_MAIN_WINDOW_GET_PRIVATE(self); + + priv->send_receive_in_progress = FALSE; + + action = modest_window_get_action (MODEST_WINDOW(self), "/MenuBar/ToolsMenu/ToolsSendReceiveMainMenu/ToolsSendReceiveAllMenu"); + gtk_action_set_sensitive (action, TRUE); +/* action = modest_window_get_action (MODEST_WINDOW(self), "/MenuBar/ToolsMenu/ToolsSendReceiveMainMenu/ToolsSendReceiveCancelSendingMenu"); */ +/* gtk_action_set_sensitive (action, TRUE); */ + widget = modest_window_get_action_widget (MODEST_WINDOW(self), "/MenuBar/ToolsMenu/ToolsSendReceiveMainMenu/ToolsMenuAdditions"); + gtk_widget_set_sensitive (widget, TRUE); +} + + +static void +on_msg_count_changed (ModestHeaderView *header_view, + TnyFolder *folder, + TnyFolderChange *change, + ModestMainWindow *main_window) +{ + gboolean refilter = FALSE; + gboolean folder_empty = FALSE; + gboolean all_marked_as_deleted = FALSE; + ModestMainWindowPrivate *priv; + + g_return_if_fail (MODEST_IS_MAIN_WINDOW (main_window)); + g_return_if_fail (TNY_IS_FOLDER(folder)); + priv = MODEST_MAIN_WINDOW_GET_PRIVATE (main_window); + + if (change != NULL) { + TnyFolderChangeChanged changed; + + changed = tny_folder_change_get_changed (change); + /* If something changes */ + if ((changed) & TNY_FOLDER_CHANGE_CHANGED_ALL_COUNT) + folder_empty = (((guint) tny_folder_change_get_new_all_count (change)) == 0); + else + folder_empty = (((guint) tny_folder_get_all_count (TNY_FOLDER (folder))) == 0); + + /* Play a sound (if configured) and make the LED blink */ + if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) { + modest_platform_push_email_notification (); + } + + if ((changed) & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS) + refilter = TRUE; + } else { + folder_empty = (((guint) tny_folder_get_all_count (TNY_FOLDER (folder))) == 0); + } + + /* Check if all messages are marked to be deleted */ + all_marked_as_deleted = modest_header_view_is_empty (header_view); + folder_empty = folder_empty || all_marked_as_deleted; + + /* Set contents style of headers view */ + if (folder_empty) { + modest_main_window_set_contents_style (main_window, + MODEST_MAIN_WINDOW_CONTENTS_STYLE_EMPTY); + gtk_widget_grab_focus (GTK_WIDGET (priv->folder_view)); + } else { + modest_main_window_set_contents_style (main_window, + MODEST_MAIN_WINDOW_CONTENTS_STYLE_HEADERS); + } + + if (refilter) + modest_header_view_refilter (header_view); +} + + +void +modest_main_window_set_contents_style (ModestMainWindow *self, + ModestMainWindowContentsStyle style) +{ + ModestMainWindowPrivate *priv; + + g_return_if_fail (MODEST_IS_MAIN_WINDOW (self)); + + priv = MODEST_MAIN_WINDOW_GET_PRIVATE(self); + + /* We allow to set the same content style than the previously + set if there are details, because it could happen when we're + selecting different accounts consecutively */ + if ((priv->contents_style == style) && + (priv->contents_style != MODEST_MAIN_WINDOW_CONTENTS_STYLE_DETAILS)) + return; + + /* Remove previous child. Delete it if it was an account + details widget */ + GtkWidget *content = gtk_bin_get_child (GTK_BIN (priv->contents_widget)); + if (content) { + if (priv->contents_style == MODEST_MAIN_WINDOW_CONTENTS_STYLE_EMPTY) { + gtk_container_remove (GTK_CONTAINER (content), priv->empty_view); + } + + gtk_container_remove (GTK_CONTAINER (priv->contents_widget), content); + } + + priv->contents_style = style; + + switch (priv->contents_style) { + case MODEST_MAIN_WINDOW_CONTENTS_STYLE_HEADERS: + wrap_in_scrolled_window (priv->contents_widget, GTK_WIDGET (priv->header_view)); + modest_maemo_set_thumbable_scrollbar (GTK_SCROLLED_WINDOW(priv->contents_widget), + TRUE); + if (priv->style == MODEST_MAIN_WINDOW_STYLE_SIMPLE) + gtk_widget_grab_focus (GTK_WIDGET (priv->header_view)); + break; + case MODEST_MAIN_WINDOW_CONTENTS_STYLE_DETAILS: + { + /* if we're started without main win, there may not be a folder + * view. this fixes a GLib-Critical */ + if (priv->folder_view) { + TnyFolderStore *selected_folderstore = + modest_folder_view_get_selected (priv->folder_view); + if (TNY_IS_ACCOUNT (selected_folderstore)) { + priv->details_widget = create_details_widget (GTK_WIDGET (self), + TNY_ACCOUNT (selected_folderstore)); + + wrap_in_scrolled_window (priv->contents_widget, + priv->details_widget); + } + if (selected_folderstore) + g_object_unref (selected_folderstore); + modest_maemo_set_thumbable_scrollbar (GTK_SCROLLED_WINDOW(priv->contents_widget), + FALSE); + } + if (priv->style == MODEST_MAIN_WINDOW_STYLE_SIMPLE) + gtk_widget_grab_focus (GTK_WIDGET (priv->details_widget)); + break; + } + case MODEST_MAIN_WINDOW_CONTENTS_STYLE_EMPTY: + wrap_in_scrolled_window (priv->contents_widget, GTK_WIDGET (priv->empty_view)); + modest_maemo_set_thumbable_scrollbar (GTK_SCROLLED_WINDOW(priv->contents_widget), + FALSE); + if (priv->style == MODEST_MAIN_WINDOW_STYLE_SIMPLE) + gtk_widget_grab_focus (GTK_WIDGET (priv->empty_view)); + break; + default: + g_return_if_reached (); + } + + /* Show */ + gtk_widget_show_all (priv->contents_widget); +} + +ModestMainWindowContentsStyle +modest_main_window_get_contents_style (ModestMainWindow *self) +{ + ModestMainWindowPrivate *priv; + + g_return_val_if_fail (MODEST_IS_MAIN_WINDOW (self), -1); + + priv = MODEST_MAIN_WINDOW_GET_PRIVATE(self); + return priv->contents_style; +} + + +static void +on_configuration_key_changed (ModestConf* conf, + const gchar *key, + ModestConfEvent event, + ModestConfNotificationId id, + ModestMainWindow *self) +{ + ModestMainWindowPrivate *priv = MODEST_MAIN_WINDOW_GET_PRIVATE(self); + TnyAccount *account = NULL; + + if (!key || strcmp (key, MODEST_CONF_DEVICE_NAME)) + return; + + if (priv->contents_style != MODEST_MAIN_WINDOW_CONTENTS_STYLE_DETAILS) + return; + + if (priv->folder_view) + account = (TnyAccount *) modest_folder_view_get_selected (priv->folder_view); + + if (account && TNY_IS_ACCOUNT (account) && + strcmp (tny_account_get_id (account), MODEST_LOCAL_FOLDERS_ACCOUNT_ID) == 0) { + GList *children; + GtkLabel *label; + const gchar *device_name; + gchar *new_text, *gray_color_markup; + + /* Get label */ + children = gtk_container_get_children (GTK_CONTAINER (priv->details_widget)); + label = GTK_LABEL (children->data); + + device_name = modest_conf_get_string (modest_runtime_get_conf(), + MODEST_CONF_DEVICE_NAME, NULL); + + gray_color_markup = get_gray_color_markup (GTK_WIDGET (self)); + new_text = create_device_name_visual_string (device_name, gray_color_markup); + + gtk_label_set_markup (label, new_text); + gtk_widget_show (GTK_WIDGET (label)); + + g_free (gray_color_markup); + g_free (new_text); + g_list_free (children); + } + g_object_unref (account); +} + +static gboolean +set_toolbar_transfer_mode (ModestMainWindow *self) +{ + ModestMainWindowPrivate *priv = NULL; + + g_return_val_if_fail (MODEST_IS_MAIN_WINDOW (self), FALSE); + + priv = MODEST_MAIN_WINDOW_GET_PRIVATE(self); + + set_toolbar_mode (self, TOOLBAR_MODE_TRANSFER); + + if (priv->progress_bar_timeout > 0) { + g_source_remove (priv->progress_bar_timeout); + priv->progress_bar_timeout = 0; + } + + return FALSE; +} + +static void +set_toolbar_mode (ModestMainWindow *self, + ModestToolBarModes mode) +{ + ModestWindowPrivate *parent_priv = NULL; + ModestMainWindowPrivate *priv = NULL; + GtkAction *sort_action = NULL, *refresh_action = NULL, *cancel_action = NULL; + + g_return_if_fail (MODEST_IS_MAIN_WINDOW (self)); + + parent_priv = MODEST_WINDOW_GET_PRIVATE(self); + priv = MODEST_MAIN_WINDOW_GET_PRIVATE(self); + + /* In case this was called before the toolbar exists: */ + if (!(parent_priv->toolbar)) + return; + + g_return_if_fail (GTK_IS_TOOLBAR(parent_priv->toolbar)); + + sort_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarSort"); + refresh_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarSendReceive"); + cancel_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarCancel"); + + /* Sets current toolbar mode */ + priv->current_toolbar_mode = mode; + + /* Checks the dimming rules */ + modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self)); + modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (self)); + + /* Show and hide toolbar items */ + switch (mode) { + case TOOLBAR_MODE_NORMAL: + if (sort_action) + gtk_action_set_visible (sort_action, TRUE); + if (refresh_action) + gtk_action_set_visible (refresh_action, TRUE); + if (priv->progress_toolitem) { + gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE); + gtk_widget_hide (priv->progress_toolitem); + } + if (priv->progress_bar) + gtk_widget_hide (priv->progress_bar); + + if (cancel_action) + gtk_action_set_visible (cancel_action, FALSE); + + /* Hide toolbar if optimized view is enabled */ + if (priv->optimized_view) + gtk_widget_hide (GTK_WIDGET(parent_priv->toolbar)); + break; + case TOOLBAR_MODE_TRANSFER: + if (sort_action) + gtk_action_set_visible (sort_action, FALSE); + if (refresh_action) + gtk_action_set_visible (refresh_action, FALSE); + if (cancel_action) + gtk_action_set_visible (cancel_action, TRUE); + if (priv->progress_bar) + gtk_widget_show (priv->progress_bar); + if (priv->progress_toolitem) { + gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE); + gtk_widget_show (priv->progress_toolitem); + } + + /* Show toolbar if it's hiden (optimized view ) */ + if (priv->optimized_view) + gtk_widget_show (GTK_WIDGET (parent_priv->toolbar)); + break; + default: + g_return_if_reached (); + } +} + +gboolean +modest_main_window_transfer_mode_enabled (ModestMainWindow *self) +{ + ModestMainWindowPrivate *priv; + + g_return_val_if_fail (MODEST_IS_MAIN_WINDOW (self), FALSE); + priv = MODEST_MAIN_WINDOW_GET_PRIVATE(self); + + return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER; +} + +static void +cancel_progressbar (GtkToolButton *toolbutton, + ModestMainWindow *self) +{ + GSList *tmp; + ModestMainWindowPrivate *priv; + + priv = MODEST_MAIN_WINDOW_GET_PRIVATE(self); + + /* Get operation observers and cancel all the operations */ + tmp = priv->progress_widgets; + while (tmp) { + modest_progress_object_cancel_all_operations (MODEST_PROGRESS_OBJECT(tmp->data)); + tmp=g_slist_next(tmp); + } +} + +static gboolean +observers_empty (ModestMainWindow *self) +{ + GSList *tmp = NULL; + ModestMainWindowPrivate *priv; + gboolean is_empty = TRUE; + guint pending_ops = 0; + + priv = MODEST_MAIN_WINDOW_GET_PRIVATE(self); + tmp = priv->progress_widgets; + + /* Check all observers */ + while (tmp && is_empty) { + pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data)); + is_empty = pending_ops == 0; + + tmp = g_slist_next(tmp); + } + + return is_empty; +} + + +/** + * Gets the toolbar mode needed for each mail operation. It stores in + * @mode_changed if the toolbar mode has changed or not + */ +static ModestToolBarModes +get_toolbar_mode_from_mail_operation (ModestMainWindow *self, + ModestMailOperation *mail_op, + gboolean *mode_changed) +{ + ModestToolBarModes mode; + ModestMainWindowPrivate *priv; + + *mode_changed = FALSE; + priv = MODEST_MAIN_WINDOW_GET_PRIVATE (self); + + /* Get toolbar mode from operation id*/ + switch (modest_mail_operation_get_type_operation (mail_op)) { + case MODEST_MAIL_OPERATION_TYPE_SEND_AND_RECEIVE: + case MODEST_MAIL_OPERATION_TYPE_RECEIVE: + mode = TOOLBAR_MODE_TRANSFER; + if (priv->current_toolbar_mode == TOOLBAR_MODE_NORMAL) + *mode_changed = TRUE; + break; + default: + mode = TOOLBAR_MODE_NORMAL; + } + return mode; +} + +static void +on_mail_operation_started (ModestMailOperation *mail_op, + gpointer user_data) +{ + ModestMainWindow *self; + ModestMailOperationTypeOperation op_type; + ModestMainWindowPrivate *priv; + ModestToolBarModes mode; + GSList *tmp; + gboolean mode_changed = FALSE; + TnyAccount *account = NULL; + + self = MODEST_MAIN_WINDOW (user_data); + priv = MODEST_MAIN_WINDOW_GET_PRIVATE (self); + + /* Do not show progress for receiving operations if the + account is the local account or the MMC one */ + op_type = modest_mail_operation_get_type_operation (mail_op); + account = modest_mail_operation_get_account (mail_op); + if (account && op_type == MODEST_MAIL_OPERATION_TYPE_OPEN) { + gboolean is_remote; + + is_remote = !(modest_tny_account_is_virtual_local_folders (account) || + modest_tny_account_is_memory_card_account (account)); + if (!is_remote) { + g_object_unref (account); + return; + } + + /* Show information banner. Remove old timeout */ + if (priv->opening_banner_timeout > 0) { + g_source_remove (priv->opening_banner_timeout); + priv->opening_banner_timeout = 0; + } + /* Create a new timeout */ + priv->opening_banner_timeout = + g_timeout_add (2000, show_opening_banner, self); + } + + /* Not every mail operation has account, noop does not */ + if (account) + g_object_unref (account); + + /* Get toolbar mode from operation id*/ + mode = get_toolbar_mode_from_mail_operation (self, mail_op, &mode_changed); + + /* Add operation observers and change toolbar if neccessary*/ + tmp = priv->progress_widgets; + if (mode == TOOLBAR_MODE_TRANSFER) { + if (mode_changed) { + GObject *source = modest_mail_operation_get_source(mail_op); + if (G_OBJECT (self) == source) { + set_toolbar_transfer_mode(self); + } + g_object_unref (source); + } + + while (tmp) { + modest_progress_object_add_operation (MODEST_PROGRESS_OBJECT (tmp->data), + mail_op); + tmp = g_slist_next (tmp); + } + } + + /* Update the main menu as well, we need to explicitely do + this in order to enable/disable accelerators */ + modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (self)); +} + +static void +on_mail_operation_finished (ModestMailOperation *mail_op, + gpointer user_data) +{ + ModestToolBarModes mode; + ModestMailOperationTypeOperation op_type; + GSList *tmp = NULL; + ModestMainWindow *self; + gboolean mode_changed; + TnyAccount *account = NULL; + ModestMainWindowPrivate *priv; + + self = MODEST_MAIN_WINDOW (user_data); + priv = MODEST_MAIN_WINDOW_GET_PRIVATE (self); + + /* The mail operation was not added to the progress objects if + the account was the local account or the MMC one */ + op_type = modest_mail_operation_get_type_operation (mail_op); + account = modest_mail_operation_get_account (mail_op); + if (account && op_type == MODEST_MAIL_OPERATION_TYPE_OPEN) { + gboolean is_remote; + + is_remote = !(modest_tny_account_is_virtual_local_folders (account) || + modest_tny_account_is_memory_card_account (account)); + if (!is_remote) { + g_object_unref (account); + return; + } + + /* Remove old timeout */ + if (priv->opening_banner_timeout > 0) { + g_source_remove (priv->opening_banner_timeout); + priv->opening_banner_timeout = 0; + } + + /* Remove the banner if exists */ + if (priv->opening_banner) { + gtk_widget_destroy (priv->opening_banner); + priv->opening_banner = NULL; + } + } + + /* Not every mail operation has account, noop does not */ + if (account) + g_object_unref (account); + + /* Get toolbar mode from operation id*/ + mode = get_toolbar_mode_from_mail_operation (self, mail_op, &mode_changed); + + /* Change toolbar mode */ + tmp = priv->progress_widgets; + if (mode == TOOLBAR_MODE_TRANSFER) { + while (tmp) { + modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data), + mail_op); + tmp = g_slist_next (tmp); + } + + /* If no more operations are being observed, NORMAL mode is enabled again */ + if (observers_empty (self)) { + set_toolbar_mode (self, TOOLBAR_MODE_NORMAL); + } + } +} + +static void +on_queue_changed (ModestMailOperationQueue *queue, + ModestMailOperation *mail_op, + ModestMailOperationQueueNotification type, + ModestMainWindow *self) +{ + ModestMainWindowPrivate *priv; + + priv = MODEST_MAIN_WINDOW_GET_PRIVATE (self); + + if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) { + priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers, + G_OBJECT (mail_op), + "operation-started", + G_CALLBACK (on_mail_operation_started), + self); + priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers, + G_OBJECT (mail_op), + "operation-finished", + G_CALLBACK (on_mail_operation_finished), + self); + } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) { + priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers, + G_OBJECT (mail_op), + "operation-started"); + priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers, + G_OBJECT (mail_op), + "operation-finished"); + } +} + +static void +set_account_visible(ModestMainWindow *self, const gchar *acc_name) +{ + ModestMainWindowPrivate *priv = MODEST_MAIN_WINDOW_GET_PRIVATE(self); + GtkAction *action; + ModestAccountMgr *mgr; + ModestAccountSettings *settings; + ModestServerAccountSettings *store_settings = NULL; + + /* Get account data */ + mgr = modest_runtime_get_account_mgr (); + settings = modest_account_mgr_load_account_settings (mgr, acc_name); + if (settings) + store_settings = modest_account_settings_get_store_settings (settings); + + /* Set the new visible & active account */ + if (settings && (modest_server_account_settings_get_account_name (store_settings)!= NULL)) { + const gchar *account_name; + + account_name = modest_account_settings_get_account_name (settings); + + modest_folder_view_set_account_id_of_visible_server_account + (priv->folder_view, + modest_server_account_settings_get_account_name (store_settings)); + modest_folder_view_select_first_inbox_or_local (priv->folder_view); + modest_window_set_active_account (MODEST_WINDOW (self), account_name); + + action = gtk_action_group_get_action (priv->view_additions_group, account_name); + if (action != NULL) { + if (!gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) { + modest_utils_toggle_action_set_active_block_notify ( + GTK_TOGGLE_ACTION (action), + TRUE); + } + } + } + + /* Free */ + if (settings) { + g_object_unref (store_settings); + g_object_unref (settings); + } +} + +/* Make sure that at least one account is "viewed": */ +static void +set_at_least_one_account_visible(ModestMainWindow *self) +{ + ModestMainWindowPrivate *priv = MODEST_MAIN_WINDOW_GET_PRIVATE(self); + ModestAccountMgr *account_mgr = modest_runtime_get_account_mgr(); + + if (!(priv->folder_view)) { + /* It is too early to do this. */ + return; + } + + const gchar *active_server_account_name = + modest_folder_view_get_account_id_of_visible_server_account (priv->folder_view); + + if (!active_server_account_name || + !modest_account_mgr_account_exists (account_mgr, active_server_account_name, TRUE)) + { + gchar* first_modest_name = modest_account_mgr_get_first_account_name (account_mgr); + gchar* default_modest_name = modest_account_mgr_get_default_account (account_mgr); + if (default_modest_name) { + set_account_visible (self, default_modest_name); + } else if (first_modest_name) { + set_account_visible (self, first_modest_name); + } + g_free (first_modest_name); + g_free (default_modest_name); + } +} + +static void +on_show_account_action_toggled (GtkToggleAction *action, + gpointer user_data) +{ + ModestMainWindow *self = MODEST_MAIN_WINDOW (user_data); + + const gchar *acc_name = gtk_action_get_name (GTK_ACTION (action)); + if (gtk_toggle_action_get_active (action)) + set_account_visible (self, acc_name); +} + +static void +refresh_account (const gchar *account_name) +{ + ModestWindow *win; + + /* win must already exists here, obviously */ + win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr (), + FALSE); + if (!win) { + g_warning ("%s: BUG: no main window!", __FUNCTION__); + return; + } + + /* If account_name == NULL, we must update all (option All) */ + if (!account_name) + modest_ui_actions_do_send_receive_all (win, TRUE, TRUE, TRUE); + else + modest_ui_actions_do_send_receive (account_name, TRUE, TRUE, TRUE, win); + +} + +static void +on_refresh_account_action_activated (GtkAction *action, + gpointer user_data) +{ + refresh_account ((const gchar*) user_data); +} + +static void +on_send_receive_csm_activated (GtkMenuItem *item, + gpointer user_data) +{ + refresh_account ((const gchar*) user_data); +} + +static gboolean +on_zoom_minus_plus_not_implemented (ModestWindow *window) +{ + g_return_val_if_fail (MODEST_IS_MAIN_WINDOW (window), FALSE); + + hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_cannot_zoom_here")); + return FALSE; + +} + +static gboolean +on_folder_view_focus_in (GtkWidget *widget, GdkEventFocus *event, gpointer userdata) +{ + ModestMainWindow *main_window = NULL; + + g_return_val_if_fail (MODEST_IS_MAIN_WINDOW (userdata), FALSE); + main_window = MODEST_MAIN_WINDOW (userdata); + + /* Update toolbar dimming state */ + modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (main_window)); + modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (main_window)); + + return FALSE; +} + +static gboolean +on_header_view_focus_in (GtkWidget *widget, + GdkEventFocus *event, + gpointer userdata) +{ + ModestMainWindow *main_window = NULL; + + g_return_val_if_fail (MODEST_IS_MAIN_WINDOW (userdata), FALSE); + + main_window = MODEST_MAIN_WINDOW (userdata); + + /* Update toolbar dimming state */ + modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (main_window)); + modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (main_window)); + + return FALSE; +} + +static void +on_folder_selection_changed (ModestFolderView *folder_view, + TnyFolderStore *folder_store, + gboolean selected, + ModestMainWindow *main_window) +{ + ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (main_window); + GtkAction *action = NULL; + gboolean show_reply = TRUE; + gboolean show_forward = TRUE; + gboolean show_cancel_send = FALSE; + gboolean show_clipboard = TRUE; + gboolean show_delete = TRUE; + + if (selected) { + if (TNY_IS_ACCOUNT (folder_store)) { + show_reply = show_forward = show_cancel_send = show_clipboard = show_delete = FALSE; + } else if (TNY_IS_FOLDER (folder_store)) { + if (modest_tny_folder_is_local_folder (TNY_FOLDER (folder_store))) { + TnyFolderType folder_type = modest_tny_folder_get_local_or_mmc_folder_type ( + TNY_FOLDER (folder_store)); + switch (folder_type) { + case TNY_FOLDER_TYPE_DRAFTS: + show_clipboard = show_delete = TRUE; + show_reply = show_forward = show_cancel_send = FALSE; + break; + case TNY_FOLDER_TYPE_SENT: + show_forward = show_clipboard = show_delete = TRUE; + show_reply = show_cancel_send = FALSE; + break; + case TNY_FOLDER_TYPE_OUTBOX: + show_clipboard = show_delete = show_cancel_send = TRUE; + show_reply = show_forward = FALSE; + break; + case TNY_FOLDER_TYPE_INVALID: + g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__); + break; + default: + show_reply = show_forward = show_clipboard = show_delete = TRUE; + show_cancel_send = FALSE; + } + } else { + show_reply = show_forward = show_clipboard = show_delete = TRUE; + show_cancel_send = FALSE; + } + } + } + + action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/HeaderViewCSM/HeaderViewCSMReply"); + gtk_action_set_visible (action, show_reply); + action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/HeaderViewCSM/HeaderViewCSMReplyAll"); + gtk_action_set_visible (action, show_reply); + action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/HeaderViewCSM/HeaderViewCSMForward"); + gtk_action_set_visible (action, show_forward); + action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/HeaderViewCSM/HeaderViewCSMCancelSending"); + gtk_action_set_visible (action, show_cancel_send); + action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/HeaderViewCSM/HeaderViewCSMDelete"); + gtk_action_set_visible (action, show_delete); + + /* We finally call to the ui actions handler, after updating properly + * the header view CSM */ + modest_ui_actions_on_folder_selection_changed (folder_view, folder_store, selected, main_window); +} + +gboolean +modest_main_window_on_msg_view_window_msg_changed (ModestMsgViewWindow *view_window, + GtkTreeModel *model, + GtkTreeRowReference *row_reference, + ModestMainWindow *self) +{ + ModestMainWindowPrivate *priv = NULL; + GtkTreeModel *header_model = NULL; + GtkTreePath *path = NULL; + + g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (view_window), FALSE); + g_return_val_if_fail (MODEST_IS_MAIN_WINDOW (self), FALSE); + g_return_val_if_fail (gtk_tree_row_reference_valid (row_reference), FALSE); + + priv = MODEST_MAIN_WINDOW_GET_PRIVATE (self); + header_model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->header_view)); + + /* Do nothing if we changed the folder in the main view */ + if (header_model != model) + return FALSE; + + /* Select the message in the header view */ + path = gtk_tree_row_reference_get_path (row_reference); + gtk_tree_view_set_cursor (GTK_TREE_VIEW (priv->header_view), + path, NULL, FALSE); + gtk_tree_path_free (path); + + return TRUE; +} + +static void +updating_banner_destroyed (gpointer data, + GObject *where_the_object_was) +{ + ModestMainWindowPrivate *priv = NULL; + + priv = MODEST_MAIN_WINDOW_GET_PRIVATE (data); + + priv->updating_banner = NULL; +} + +static gboolean +show_updating_banner (gpointer user_data) +{ + ModestMainWindowPrivate *priv = NULL; + + priv = MODEST_MAIN_WINDOW_GET_PRIVATE (user_data); + + if (priv->updating_banner == NULL) { + + /* We're outside the main lock */ + gdk_threads_enter (); + priv->updating_banner = + modest_platform_animation_banner (GTK_WIDGET (user_data), NULL, + _CS ("ckdg_pb_updating")); + + /* We need this because banners in Maemo could be + destroyed by dialogs so we need to properly update + our reference to it */ + g_object_weak_ref (G_OBJECT (priv->updating_banner), + updating_banner_destroyed, + user_data); + gdk_threads_leave (); + } + + /* Remove timeout */ + priv->updating_banner_timeout = 0; + return FALSE; +} + +/** + * We use this function to show/hide a progress banner showing + * "Updating" while the header view is being filled. We're not showing + * it unless the update takes more than 2 seconds + * + * If starting = TRUE then the refresh is starting, otherwise it means + * that is has just finished + */ +static void +on_updating_msg_list (ModestHeaderView *header_view, + gboolean starting, + gpointer user_data) +{ + ModestMainWindowPrivate *priv = NULL; + + priv = MODEST_MAIN_WINDOW_GET_PRIVATE (user_data); + + /* Remove old timeout */ + if (priv->updating_banner_timeout > 0) { + g_source_remove (priv->updating_banner_timeout); + priv->updating_banner_timeout = 0; + } + + /* Create a new timeout */ + if (starting) { + priv->updating_banner_timeout = + g_timeout_add (2000, show_updating_banner, user_data); + } else { + /* Remove the banner if exists */ + if (priv->updating_banner) { + gtk_widget_destroy (priv->updating_banner); + priv->updating_banner = NULL; + } + } +} + +gboolean +modest_main_window_screen_is_on (ModestMainWindow *self) +{ + ModestMainWindowPrivate *priv = NULL; + + g_return_val_if_fail (MODEST_IS_MAIN_WINDOW(self), FALSE); + + priv = MODEST_MAIN_WINDOW_GET_PRIVATE (self); + + return (priv->display_state == OSSO_DISPLAY_ON) ? TRUE : FALSE; +} + +static void +remove_banners (ModestMainWindow *window) +{ + ModestMainWindowPrivate *priv; + + priv = MODEST_MAIN_WINDOW_GET_PRIVATE (window); + + if (priv->opening_banner_timeout > 0) { + g_source_remove (priv->opening_banner_timeout); + priv->opening_banner_timeout = 0; + } + + if (priv->opening_banner != NULL) { + gtk_widget_destroy (priv->opening_banner); + priv->opening_banner = NULL; + } + + if (priv->updating_banner_timeout > 0) { + g_source_remove (priv->updating_banner_timeout); + priv->updating_banner_timeout = 0; + } + + if (priv->updating_banner != NULL) { + gtk_widget_destroy (priv->updating_banner); + priv->updating_banner = NULL; + } +} + + +static void +on_window_hide (GObject *gobject, + GParamSpec *arg1, + gpointer user_data) +{ + g_return_if_fail (MODEST_IS_MAIN_WINDOW (gobject)); + + if (!GTK_WIDGET_VISIBLE (gobject)) { + TnyFolderStore *folder_store; + ModestMainWindowPrivate *priv; + + /* Remove the currently shown banners */ + remove_banners (MODEST_MAIN_WINDOW (gobject)); + + /* Force the folder view to sync the currently selected folder + to save the read/unread status and to expunge messages */ + priv = MODEST_MAIN_WINDOW_GET_PRIVATE (gobject); + folder_store = modest_folder_view_get_selected (priv->folder_view); + if (TNY_IS_FOLDER (folder_store)) { + ModestMailOperation *mail_op; + + mail_op = modest_mail_operation_new (NULL); + modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), + mail_op); + modest_mail_operation_sync_folder (mail_op, TNY_FOLDER (folder_store), FALSE); + g_object_unref (mail_op); + g_object_unref (folder_store); + } + } +} + +static void +on_window_destroy (GtkObject *widget, + gpointer user_data) +{ + g_return_if_fail (MODEST_IS_MAIN_WINDOW (widget)); + + remove_banners (MODEST_MAIN_WINDOW (widget)); +} + +static void +opening_banner_destroyed (gpointer data, + GObject *where_the_object_was) +{ + ModestMainWindowPrivate *priv = NULL; + + priv = MODEST_MAIN_WINDOW_GET_PRIVATE (data); + + priv->opening_banner = NULL; +} + +static gboolean +show_opening_banner (gpointer user_data) +{ + ModestMainWindowPrivate *priv = NULL; + + priv = MODEST_MAIN_WINDOW_GET_PRIVATE (user_data); + + if (priv->opening_banner == NULL) { + + /* We're outside the main lock */ + gdk_threads_enter (); + priv->opening_banner = + modest_platform_animation_banner (GTK_WIDGET (user_data), NULL, + _("mail_me_opening")); + + /* We need this because banners in Maemo could be + destroyed by dialogs so we need to properly update + our reference to it */ + g_object_weak_ref (G_OBJECT (priv->opening_banner), + opening_banner_destroyed, + user_data); + + /* We need this because banners in Maemo could be + destroyed by dialogs so we need to properly update + our reference to it */ + g_object_weak_ref (G_OBJECT (priv->updating_banner), + updating_banner_destroyed, + user_data); + gdk_threads_leave (); + } + + /* Remove timeout */ + priv->opening_banner_timeout = 0; + return FALSE; +} diff --git a/src/hildon2/modest-msg-edit-window-ui-dimming.h b/src/hildon2/modest-msg-edit-window-ui-dimming.h new file mode 100644 index 0000000..6144a7b --- /dev/null +++ b/src/hildon2/modest-msg-edit-window-ui-dimming.h @@ -0,0 +1,50 @@ +#ifndef __MODEST_MSG_EDIT_WINDOW_UI_DIMMING_PRIV_H__ +#define __MODEST_MSG_EDIT_WINDOW_UI_DIMMING_PRIV_H__ + +#include "modest-dimming-rules-group.h" +#include "modest-ui-dimming-rules.h" + +G_BEGIN_DECLS + + +/* Menu Dimming rules entries */ +static const ModestDimmingEntry modest_msg_edit_window_menu_dimming_entries [] = { + + /* Format Menu */ + { "/MenuBar/FormatMenu/SelectFontMenu", G_CALLBACK (modest_ui_dimming_rules_on_set_style) }, + { "/MenuBar/FormatMenu/AlignmentMenu", G_CALLBACK (modest_ui_dimming_rules_on_set_style) }, + { "/MenuBar/FormatMenu/AlignmentLeftMenu", G_CALLBACK (modest_ui_dimming_rules_on_set_style) }, + { "/MenuBar/FormatMenu/AlignmentRightMenu", G_CALLBACK (modest_ui_dimming_rules_on_set_style) }, + { "/MenuBar/FormatMenu/AlignmentCenterMenu", G_CALLBACK (modest_ui_dimming_rules_on_set_style) }, + { "/MenuBar/FormatMenu/InsertImageMenu", G_CALLBACK (modest_ui_dimming_rules_on_set_style) }, + { "/MenuBar/EditMenu/SelectAllMenu", G_CALLBACK (modest_ui_dimming_rules_on_select_all) }, + { "/MenuBar/EditMenu/UndoMenu", G_CALLBACK (modest_ui_dimming_rules_on_undo) }, + { "/MenuBar/EditMenu/RedoMenu", G_CALLBACK (modest_ui_dimming_rules_on_redo) }, + { "/MenuBar/EditMenu/PasteMenu", G_CALLBACK (modest_ui_dimming_rules_on_editor_paste) }, + { "/MenuBar/AttachmentsMenu/RemoveAttachmentsMenu", G_CALLBACK (modest_ui_dimming_rules_on_editor_remove_attachment) }, + { "/MenuBar/AttachmentsMenu/InsertImageMenu", G_CALLBACK (modest_ui_dimming_rules_on_set_style) }, + { "/MenuBar/EmailMenu/SendMenu", G_CALLBACK (modest_ui_dimming_rules_on_send) }, + { "/MenuBar/EmailMenu/SaveToDraftsMenu", G_CALLBACK (modest_ui_dimming_rules_on_save_to_drafts) }, + +}; + +/* Menu Dimming rules entries */ +static const ModestDimmingEntry modest_msg_edit_window_toolbar_dimming_entries [] = { + + /* Toolbar */ + { "/ToolBar/ToolbarSend", G_CALLBACK(modest_ui_dimming_rules_on_send) }, + { "/ToolBar/ActionsBold", G_CALLBACK(modest_ui_dimming_rules_on_set_style) }, + { "/ToolBar/ActionsItalics", G_CALLBACK(modest_ui_dimming_rules_on_set_style) }, +}; + +/* Clipboard Dimming rules entries */ +static const ModestDimmingEntry modest_msg_edit_window_clipboard_dimming_entries [] = { + + /* Toolbar */ + { "/MenuBar/EditMenu/CutMenu", G_CALLBACK(modest_ui_dimming_rules_on_cut) }, + { "/MenuBar/EditMenu/CopyMenu", G_CALLBACK(modest_ui_dimming_rules_on_copy) }, + { "/MenuBar/EditMenu/PasteMenu", G_CALLBACK(modest_ui_dimming_rules_on_editor_paste) }, +}; + +G_END_DECLS +#endif /* __MODEST_MSG_VIEW_WINDOW_UI_PRIV_H__ */ diff --git a/src/hildon2/modest-msg-edit-window.c b/src/hildon2/modest-msg-edit-window.c new file mode 100644 index 0000000..511eb0c --- /dev/null +++ b/src/hildon2/modest-msg-edit-window.c @@ -0,0 +1,3679 @@ +/* Copyright (c) 2006, Nokia Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Nokia Corporation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include + +#include + +#include "modest-platform.h" +#include "modest-icon-names.h" +#include "modest-widget-memory.h" +#include "modest-window-priv.h" +#include "modest-mail-operation.h" +#include "modest-tny-platform-factory.h" +#include "modest-tny-msg.h" +#include "modest-tny-folder.h" +#include "modest-tny-account.h" +#include "modest-address-book.h" +#include "modest-text-utils.h" +#include +#include +#include +#include "modest-scroll-area.h" +#include "modest-msg-edit-window-ui-dimming.h" + +#include "modest-hildon-includes.h" +#ifdef MODEST_HAVE_HILDON0_WIDGETS +#include +#endif +#include "widgets/modest-msg-edit-window-ui.h" +#ifdef MODEST_HAVE_HILDON0_WIDGETS +#include +#else +#include +#endif +#include +#include "modest-maemo-utils.h" + + +#define DEFAULT_FONT_SIZE 3 +#define DEFAULT_FONT 2 +#define DEFAULT_SIZE_BUTTON_FONT_FAMILY "Sans" +#define DEFAULT_SIZE_COMBOBOX_WIDTH 80 +#define DEFAULT_MAIN_VBOX_SPACING 6 +#define SUBJECT_MAX_LENGTH 1000 +#define IMAGE_MAX_WIDTH 560 +#define DEFAULT_FONT_SCALE 1.5 + +static gboolean is_wp_text_buffer_started = FALSE; + +static void modest_msg_edit_window_class_init (ModestMsgEditWindowClass *klass); +static void modest_msg_edit_window_init (ModestMsgEditWindow *obj); +static void modest_msg_edit_window_finalize (GObject *obj); + +static gboolean msg_body_focus (GtkWidget *focus, GdkEventFocus *event, gpointer userdata); +static void body_changed (GtkTextBuffer *buffer, ModestMsgEditWindow *editor); +static void recpt_field_changed (GtkTextBuffer *buffer, ModestMsgEditWindow *editor); + +static void text_buffer_refresh_attributes (WPTextBuffer *buffer, ModestMsgEditWindow *window); +static void text_buffer_can_undo (GtkTextBuffer *buffer, gboolean can_undo, ModestMsgEditWindow *window); +static void text_buffer_can_redo (GtkTextBuffer *buffer, gboolean can_redo, ModestMsgEditWindow *window); +static void text_buffer_apply_tag (GtkTextBuffer *buffer, GtkTextTag *tag, + GtkTextIter *start, GtkTextIter *end, + gpointer userdata); +static void text_buffer_delete_images_by_id (GtkTextBuffer *buffer, const gchar * image_id); +static void subject_field_changed (GtkEditable *editable, ModestMsgEditWindow *window); +static void subject_field_insert_text (GtkEditable *editable, + gchar *new_text, + gint new_text_length, + gint *position, + ModestMsgEditWindow *window); +static void modest_msg_edit_window_color_button_change (ModestMsgEditWindow *window, + gpointer userdata); +static void modest_msg_edit_window_size_change (GtkCheckMenuItem *menu_item, + gpointer userdata); +static void modest_msg_edit_window_font_change (GtkCheckMenuItem *menu_item, + gpointer userdata); +static void modest_msg_edit_window_setup_toolbar (ModestMsgEditWindow *window); +static gboolean modest_msg_edit_window_window_state_event (GtkWidget *widget, + GdkEventWindowState *event, + gpointer userdata); +static void modest_msg_edit_window_open_addressbook (ModestMsgEditWindow *window, + ModestRecptEditor *editor); +static void modest_msg_edit_window_add_attachment_clicked (GtkButton *button, + ModestMsgEditWindow *window); + +/* ModestWindow methods implementation */ +static void modest_msg_edit_window_disconnect_signals (ModestWindow *window); +static void modest_msg_edit_window_show_toolbar (ModestWindow *window, + gboolean show_toolbar); +static void modest_msg_edit_window_clipboard_owner_change (GtkClipboard *clipboard, + GdkEvent *event, + ModestMsgEditWindow *window); +static void modest_msg_edit_window_clipboard_owner_handle_change_in_idle (ModestMsgEditWindow *window); +static void subject_field_move_cursor (GtkEntry *entry, + GtkMovementStep step, + gint a1, + gboolean a2, + gpointer userdata); +static void update_window_title (ModestMsgEditWindow *window); + +/* Find toolbar */ +static void modest_msg_edit_window_find_toolbar_search (GtkWidget *widget, + ModestMsgEditWindow *window); +static void modest_msg_edit_window_find_toolbar_close (GtkWidget *widget, + ModestMsgEditWindow *window); +static gboolean gtk_text_iter_forward_search_insensitive (const GtkTextIter *iter, + const gchar *str, + GtkTextIter *match_start, + GtkTextIter *match_end); + +static void remove_tags (WPTextBuffer *buffer); + +static void on_account_removed (TnyAccountStore *account_store, + TnyAccount *account, + gpointer user_data); + +static gboolean on_zoom_minus_plus_not_implemented (ModestWindow *window); +static void set_zoom_do_nothing (ModestWindow *window, gdouble zoom); +static gdouble get_zoom_do_nothing (ModestWindow *window); + +static void init_window (ModestMsgEditWindow *obj); + +gboolean scroll_drag_timeout (gpointer userdata); +static void correct_scroll (ModestMsgEditWindow *w); +static void correct_scroll_without_drag_check (ModestMsgEditWindow *w, gboolean only_if_focused); +static void text_buffer_end_user_action (GtkTextBuffer *buffer, + ModestMsgEditWindow *userdata); +static void text_buffer_mark_set (GtkTextBuffer *buffer, + GtkTextIter *iter, + GtkTextMark *mark, + ModestMsgEditWindow *userdata); +static void vadj_changed (GtkAdjustment *adj, + ModestMsgEditWindow *window); + +static void DEBUG_BUFFER (WPTextBuffer *buffer) +{ +#ifdef DEBUG + GtkTextIter iter; + g_message ("BEGIN BUFFER OF SIZE %d", gtk_text_buffer_get_char_count (GTK_TEXT_BUFFER (buffer))); + + gtk_text_buffer_get_start_iter (GTK_TEXT_BUFFER (buffer), &iter); + while (!gtk_text_iter_is_end (&iter)) { + GString *output = g_string_new (""); + GSList *toggled_tags; + GSList *node; + + toggled_tags = gtk_text_iter_get_toggled_tags (&iter, FALSE); + g_string_append_printf (output, "%d: CLOSED [ ", gtk_text_iter_get_offset (&iter)); + for (node = toggled_tags; node != NULL; node = g_slist_next (node)) { + GtkTextTag *tag = (GtkTextTag *) node->data; + const gchar *name; + g_object_get (G_OBJECT (tag), "name", &name, NULL); + output = g_string_append (output, name); + g_string_append (output, " "); + } + output = g_string_append (output, "] OPENED [ "); + toggled_tags = gtk_text_iter_get_toggled_tags (&iter, TRUE); + for (node = toggled_tags; node != NULL; node = g_slist_next (node)) { + GtkTextTag *tag = (GtkTextTag *) node->data; + const gchar *name; + g_object_get (G_OBJECT (tag), "name", &name, NULL); + output = g_string_append (output, name); + g_string_append (output, " "); + } + output = g_string_append (output, "]\n"); + g_message ("%s", output->str); + g_string_free (output, TRUE); + gtk_text_iter_forward_to_tag_toggle (&iter, NULL); + } + g_message ("END BUFFER"); +#endif +} + + +/* static gboolean */ +/* on_key_pressed (GtkWidget *self, */ +/* GdkEventKey *event, */ +/* gpointer user_data); */ + +/* list my signals */ +enum { + /* MY_SIGNAL_1, */ + /* MY_SIGNAL_2, */ + LAST_SIGNAL +}; + +typedef struct _ModestMsgEditWindowPrivate ModestMsgEditWindowPrivate; +struct _ModestMsgEditWindowPrivate { + GtkWidget *msg_body; + GtkWidget *frame; + GtkWidget *header_box; + + ModestPairList *from_field_protos; + GtkWidget *from_field; + gchar *original_account_name; + + GtkWidget *to_field; + GtkWidget *cc_field; + GtkWidget *bcc_field; + GtkWidget *subject_field; + GtkWidget *attachments_view; + GtkWidget *priority_icon; + GtkWidget *add_attachment_button; + + GtkWidget *cc_caption; + GtkWidget *bcc_caption; + gboolean update_caption_visibility; + GtkWidget *attachments_caption; + + GtkTextBuffer *text_buffer; + + GtkWidget *font_size_toolitem; + GtkWidget *font_face_toolitem; + GtkWidget *font_color_button; + GSList *font_items_group; + GtkWidget *font_tool_button_label; + GSList *size_items_group; + GtkWidget *size_tool_button_label; + + GtkWidget *find_toolbar; + gchar *last_search; + + GtkWidget *font_dialog; + + GtkWidget *scroll; + guint scroll_drag_timeout_id; + gdouble last_upper; + + gint next_cid; + TnyList *attachments; + TnyList *images; + guint64 images_size; + gint images_count; + + TnyHeaderFlags priority_flags; + + gboolean can_undo, can_redo; + gulong clipboard_change_handler_id; + gulong default_clipboard_change_handler_id; + gulong account_removed_handler_id; + guint clipboard_owner_idle; + gchar *clipboard_text; + + TnyMsg *draft_msg; + TnyMsg *outbox_msg; + gchar *msg_uid; + + gboolean sent; +}; + +#define MODEST_MSG_EDIT_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \ + MODEST_TYPE_MSG_EDIT_WINDOW, \ + ModestMsgEditWindowPrivate)) +/* globals */ +static GtkWindowClass *parent_class = NULL; + +/* uncomment the following if you have defined any signals */ +/* static guint signals[LAST_SIGNAL] = {0}; */ + +GType +modest_msg_edit_window_get_type (void) +{ + static GType my_type = 0; + if (!my_type) { + static const GTypeInfo my_info = { + sizeof(ModestMsgEditWindowClass), + NULL, /* base init */ + NULL, /* base finalize */ + (GClassInitFunc) modest_msg_edit_window_class_init, + NULL, /* class finalize */ + NULL, /* class data */ + sizeof(ModestMsgEditWindow), + 1, /* n_preallocs */ + (GInstanceInitFunc) modest_msg_edit_window_init, + NULL + }; + my_type = g_type_register_static (MODEST_TYPE_WINDOW, + "ModestMsgEditWindow", + &my_info, 0); + + } + return my_type; +} + +static void +save_state (ModestWindow *self) +{ + modest_widget_memory_save (modest_runtime_get_conf(), + G_OBJECT(self), MODEST_CONF_EDIT_WINDOW_KEY); +} + + +static void +restore_settings (ModestMsgEditWindow *self) +{ + ModestConf *conf = NULL; + GtkAction *action; + ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (self); + + conf = modest_runtime_get_conf (); + action = gtk_ui_manager_get_action (parent_priv->ui_manager, + "/MenuBar/ViewMenu/ShowToolbarMenu/ViewShowToolbarNormalScreenMenu"); + modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), + modest_conf_get_bool (conf, MODEST_CONF_EDIT_WINDOW_SHOW_TOOLBAR, NULL)); + action = gtk_ui_manager_get_action (parent_priv->ui_manager, + "/MenuBar/ViewMenu/ShowToolbarMenu/ViewShowToolbarFullScreenMenu"); + modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), + modest_conf_get_bool (conf, MODEST_CONF_EDIT_WINDOW_SHOW_TOOLBAR_FULLSCREEN, NULL)); + + /* set initial state of cc and bcc */ + action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewCcFieldMenu"); + modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), + modest_conf_get_bool(modest_runtime_get_conf(), MODEST_CONF_SHOW_CC, NULL)); + action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewBccFieldMenu"); + modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), + modest_conf_get_bool(modest_runtime_get_conf(), MODEST_CONF_SHOW_BCC, NULL)); + + /* Dim at start clipboard actions */ + action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/EditMenu/CutMenu"); + gtk_action_set_sensitive (action, FALSE); + action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/EditMenu/CopyMenu"); + gtk_action_set_sensitive (action, FALSE); + action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/AttachmentsMenu/RemoveAttachmentsMenu"); + gtk_action_set_sensitive (action, FALSE); + + modest_widget_memory_restore (conf, + G_OBJECT(self), MODEST_CONF_EDIT_WINDOW_KEY); +} + + +static void +modest_msg_edit_window_class_init (ModestMsgEditWindowClass *klass) +{ + GObjectClass *gobject_class; + ModestWindowClass *modest_window_class; + gobject_class = (GObjectClass*) klass; + modest_window_class = (ModestWindowClass*) klass; + + parent_class = g_type_class_peek_parent (klass); + gobject_class->finalize = modest_msg_edit_window_finalize; + + modest_window_class->set_zoom_func = set_zoom_do_nothing; + modest_window_class->get_zoom_func = get_zoom_do_nothing; + modest_window_class->zoom_plus_func = on_zoom_minus_plus_not_implemented; + modest_window_class->zoom_minus_func = on_zoom_minus_plus_not_implemented; + modest_window_class->show_toolbar_func = modest_msg_edit_window_show_toolbar; + modest_window_class->save_state_func = save_state; + modest_window_class->disconnect_signals_func = modest_msg_edit_window_disconnect_signals; + + g_type_class_add_private (gobject_class, sizeof(ModestMsgEditWindowPrivate)); +} + +static void +modest_msg_edit_window_init (ModestMsgEditWindow *obj) +{ + ModestMsgEditWindowPrivate *priv; + priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE(obj); + + priv->msg_body = NULL; + priv->frame = NULL; + priv->from_field = NULL; + priv->to_field = NULL; + priv->cc_field = NULL; + priv->bcc_field = NULL; + priv->subject_field = NULL; + priv->attachments = TNY_LIST (tny_simple_list_new ()); + priv->images = TNY_LIST (tny_simple_list_new ()); + priv->images_size = 0; + priv->images_count = 0; + priv->next_cid = 0; + + priv->cc_caption = NULL; + priv->bcc_caption = NULL; + priv->update_caption_visibility = FALSE; + + priv->priority_flags = 0; + + priv->find_toolbar = NULL; + priv->last_search = NULL; + + priv->draft_msg = NULL; + priv->outbox_msg = NULL; + priv->msg_uid = NULL; + + priv->can_undo = FALSE; + priv->can_redo = FALSE; + priv->clipboard_change_handler_id = 0; + priv->default_clipboard_change_handler_id = 0; + priv->account_removed_handler_id = 0; + priv->clipboard_owner_idle = 0; + priv->clipboard_text = NULL; + priv->sent = FALSE; + + priv->scroll_drag_timeout_id = 0; + priv->last_upper = 0.0; + + priv->font_dialog = NULL; + + modest_window_mgr_register_help_id (modest_runtime_get_window_mgr(), + GTK_WINDOW(obj),"applications_email_editor"); + + if (!is_wp_text_buffer_started) { + is_wp_text_buffer_started = TRUE; + wp_text_buffer_library_init (); + } + + init_window (obj); + + hildon_program_add_window (hildon_program_get_instance(), + HILDON_WINDOW(obj)); +} + + +/* FIXME: this is a dup from the one in gtk/ */ + +/** + * @result: A ModestPairList, which must be freed with modest_pair_list_free(). + */ +static ModestPairList* +get_transports (void) +{ + GSList *transports = NULL; + + ModestAccountMgr *account_mgr = modest_runtime_get_account_mgr(); + GSList *accounts = modest_account_mgr_account_names (account_mgr, + TRUE /* only enabled accounts. */); + + GSList *cursor = accounts; + while (cursor) { + gchar *account_name = cursor->data; + gchar *from_string = NULL; + if (account_name) { + from_string = modest_account_mgr_get_from_string (account_mgr, + account_name); + } + + if (from_string && account_name) { + gchar *name = account_name; + ModestPair *pair = modest_pair_new ((gpointer) name, + (gpointer) from_string , TRUE); + transports = g_slist_prepend (transports, pair); + } + + cursor = cursor->next; + } + g_slist_free (accounts); /* only free the accounts, not the elements, + * because they are used in the pairlist */ + return transports; +} + +static void window_focus (GtkWindow *window, + GtkWidget *widget, + gpointer userdata) +{ + modest_window_check_dimming_rules_group (MODEST_WINDOW (userdata), MODEST_DIMMING_RULES_CLIPBOARD); +} + +gboolean +scroll_drag_timeout (gpointer userdata) +{ + ModestMsgEditWindow *win = (ModestMsgEditWindow *) userdata; + ModestMsgEditWindowPrivate *priv; + + priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE(win); + + correct_scroll_without_drag_check (win, TRUE); + + priv->scroll_drag_timeout_id = 0; + + return FALSE; +} + +static void +correct_scroll_without_drag_check (ModestMsgEditWindow *w, gboolean only_if_focused) +{ + ModestMsgEditWindowPrivate *priv; + GtkTextMark *insert; + GtkTextIter iter; + GdkRectangle rectangle; + GtkAdjustment *vadj; + gdouble new_value; + gint offset; + GdkWindow *window; + + priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE(w); + + if (only_if_focused && !gtk_widget_is_focus (priv->msg_body)) + return; + + insert = gtk_text_buffer_get_insert (priv->text_buffer); + gtk_text_buffer_get_iter_at_mark (priv->text_buffer, &iter, insert); + + gtk_text_view_get_iter_location (GTK_TEXT_VIEW (priv->msg_body), &iter, &rectangle); + vadj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (priv->scroll)); + offset = priv->msg_body->allocation.y; + + new_value = vadj->value; + + if ((offset + rectangle.y + rectangle.height) > + ((gint) (vadj->value +vadj->page_size))) { + new_value = (offset + rectangle.y) - vadj->page_size * 0.25; + if (new_value > vadj->upper - vadj->page_size) + new_value = vadj->upper - vadj->page_size; + } else if ((offset + rectangle.y) < ((gint) vadj->value)) { + new_value = (offset + rectangle.y - vadj->page_size * 0.75); + if (((gint) (new_value + vadj->page_size)) < (offset + rectangle.y + rectangle.height)) + new_value = offset + rectangle.y + rectangle.height - (gint) vadj->page_size; + if (new_value < 0.0) + new_value = 0.0; + if (new_value > vadj->value) + new_value = vadj->value; + } + + if (vadj->value != new_value) { + g_signal_emit_by_name (GTK_TEXT_VIEW(priv->msg_body)->layout, + "invalidated"); + vadj->value = new_value; + gtk_adjustment_value_changed (vadj); + /* invalidate body */ + window = gtk_widget_get_parent_window (priv->msg_body); + if (window) + gdk_window_invalidate_rect (window, NULL, TRUE); + } + +} + +static void +correct_scroll (ModestMsgEditWindow *w) +{ + ModestMsgEditWindowPrivate *priv; + + priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE(w); + if (gtk_grab_get_current () == priv->msg_body) { + if (priv->scroll_drag_timeout_id == 0) { + priv->scroll_drag_timeout_id = g_timeout_add (500, (GSourceFunc) scroll_drag_timeout, + (gpointer) w); + } + return; + } + + correct_scroll_without_drag_check (w, TRUE); +} + +static void +text_buffer_end_user_action (GtkTextBuffer *buffer, + ModestMsgEditWindow *userdata) +{ + + correct_scroll (userdata); +} + +static void +text_buffer_mark_set (GtkTextBuffer *buffer, + GtkTextIter *iter, + GtkTextMark *mark, + ModestMsgEditWindow *userdata) +{ + gtk_text_buffer_begin_user_action (buffer); + gtk_text_buffer_end_user_action (buffer); +} + +static void +cut_clipboard_check (GtkTextView *text_view, + gpointer userdata) +{ + GtkTextBuffer *buffer; + + buffer = gtk_text_view_get_buffer (text_view); + if (!modest_text_utils_buffer_selection_is_valid (buffer)) { + g_signal_stop_emission_by_name ((gpointer )text_view, "cut-clipboard"); + } +} + +static void +copy_clipboard_check (GtkTextView *text_view, + gpointer userdata) +{ + GtkTextBuffer *buffer; + + buffer = gtk_text_view_get_buffer (text_view); + if (!modest_text_utils_buffer_selection_is_valid (buffer)) { + g_signal_stop_emission_by_name ((gpointer )text_view, "copy-clipboard"); + } +} + +static void +vadj_changed (GtkAdjustment *adj, + ModestMsgEditWindow *window) +{ + ModestMsgEditWindowPrivate *priv; + + priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window); + + if (priv->last_upper != adj->upper) { + priv->last_upper = adj->upper; + correct_scroll (window); + } +} + +static void +attachment_deleted (ModestAttachmentsView *attachments_view, + gpointer user_data) +{ + modest_msg_edit_window_remove_attachments (MODEST_MSG_EDIT_WINDOW (user_data), + NULL); +} + +static void +connect_signals (ModestMsgEditWindow *obj) +{ + ModestMsgEditWindowPrivate *priv; + + priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE(obj); + + g_signal_connect (G_OBJECT (priv->text_buffer), "refresh_attributes", + G_CALLBACK (text_buffer_refresh_attributes), obj); + g_signal_connect (G_OBJECT (priv->text_buffer), "can-undo", + G_CALLBACK (text_buffer_can_undo), obj); + g_signal_connect (G_OBJECT (priv->text_buffer), "can-redo", + G_CALLBACK (text_buffer_can_redo), obj); + g_signal_connect (G_OBJECT (priv->text_buffer), "changed", + G_CALLBACK (body_changed), obj); + g_signal_connect (G_OBJECT (priv->text_buffer), "modified-changed", + G_CALLBACK (body_changed), obj); + g_signal_connect (G_OBJECT (obj), "window-state-event", + G_CALLBACK (modest_msg_edit_window_window_state_event), + NULL); + g_signal_connect (G_OBJECT (priv->text_buffer), "end-user-action", + G_CALLBACK (text_buffer_end_user_action), obj); + g_signal_connect (G_OBJECT (priv->text_buffer), "mark-set", + G_CALLBACK (text_buffer_mark_set), obj); + g_signal_connect_after (G_OBJECT (priv->text_buffer), "apply-tag", + G_CALLBACK (text_buffer_apply_tag), obj); + g_signal_connect_swapped (G_OBJECT (priv->to_field), "open-addressbook", + G_CALLBACK (modest_msg_edit_window_open_addressbook), obj); + g_signal_connect_swapped (G_OBJECT (priv->cc_field), "open-addressbook", + G_CALLBACK (modest_msg_edit_window_open_addressbook), obj); + g_signal_connect_swapped (G_OBJECT (priv->bcc_field), "open-addressbook", + G_CALLBACK (modest_msg_edit_window_open_addressbook), obj); + + g_signal_connect (G_OBJECT (priv->add_attachment_button), "clicked", + G_CALLBACK (modest_msg_edit_window_add_attachment_clicked), obj); + + g_signal_connect (G_OBJECT (priv->msg_body), "focus-in-event", + G_CALLBACK (msg_body_focus), obj); + g_signal_connect (G_OBJECT (priv->msg_body), "focus-out-event", + G_CALLBACK (msg_body_focus), obj); + g_signal_connect (G_OBJECT (obj), "set-focus", G_CALLBACK (window_focus), obj); + g_signal_connect (G_OBJECT (modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR (priv->to_field))), + "changed", G_CALLBACK (recpt_field_changed), obj); + g_signal_connect (G_OBJECT (modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR (priv->cc_field))), + "changed", G_CALLBACK (recpt_field_changed), obj); + g_signal_connect (G_OBJECT (modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR (priv->bcc_field))), + "changed", G_CALLBACK (recpt_field_changed), obj); + g_signal_connect (G_OBJECT (priv->subject_field), "changed", G_CALLBACK (subject_field_changed), obj); + g_signal_connect_after (G_OBJECT (priv->subject_field), "move-cursor", G_CALLBACK (subject_field_move_cursor), obj); + g_signal_connect (G_OBJECT (priv->subject_field), "insert-text", G_CALLBACK (subject_field_insert_text), obj); + + g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_edit_window_find_toolbar_close), obj); + g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_edit_window_find_toolbar_search), obj); + + g_signal_connect (G_OBJECT (gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (priv->scroll))), + "changed", + G_CALLBACK (vadj_changed), + obj); + + priv->clipboard_change_handler_id = + g_signal_connect (G_OBJECT (gtk_clipboard_get (GDK_SELECTION_PRIMARY)), "owner-change", + G_CALLBACK (modest_msg_edit_window_clipboard_owner_change), obj); + priv->default_clipboard_change_handler_id = + g_signal_connect (G_OBJECT (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD)), "owner-change", + G_CALLBACK (modest_msg_edit_window_clipboard_owner_change), obj); + + g_signal_connect (G_OBJECT (priv->msg_body), "cut-clipboard", G_CALLBACK (cut_clipboard_check), NULL); + g_signal_connect (G_OBJECT (priv->msg_body), "copy-clipboard", G_CALLBACK (copy_clipboard_check), NULL); + g_signal_connect (G_OBJECT (priv->attachments_view), "delete", G_CALLBACK (attachment_deleted), obj); +} + +static void +init_window (ModestMsgEditWindow *obj) +{ + GtkWidget *from_caption, *to_caption, *subject_caption; + GtkWidget *main_vbox; + ModestMsgEditWindowPrivate *priv; + GtkActionGroup *action_group; + ModestWindowPrivate *parent_priv; + GdkPixbuf *window_icon = NULL; + GError *error = NULL; + + GtkSizeGroup *size_group; + GtkWidget *subject_box; + GtkWidget *attachment_icon; + GtkWidget *window_box; +#if (GTK_MINOR_VERSION >= 10) + GdkAtom deserialize_type; +#endif + priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE(obj); + parent_priv = MODEST_WINDOW_GET_PRIVATE (obj); + + parent_priv->ui_manager = gtk_ui_manager_new(); + action_group = gtk_action_group_new ("ModestMsgEditWindowActions"); + gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE); + + /* Add common actions */ + gtk_action_group_add_actions (action_group, + modest_msg_edit_action_entries, + G_N_ELEMENTS (modest_msg_edit_action_entries), + obj); + gtk_action_group_add_toggle_actions (action_group, + modest_msg_edit_toggle_action_entries, + G_N_ELEMENTS (modest_msg_edit_toggle_action_entries), + obj); + gtk_action_group_add_radio_actions (action_group, + modest_msg_edit_alignment_radio_action_entries, + G_N_ELEMENTS (modest_msg_edit_alignment_radio_action_entries), + GTK_JUSTIFY_LEFT, + G_CALLBACK (modest_ui_actions_on_change_justify), + obj); + gtk_action_group_add_radio_actions (action_group, + modest_msg_edit_priority_action_entries, + G_N_ELEMENTS (modest_msg_edit_priority_action_entries), + 0, + G_CALLBACK (modest_ui_actions_msg_edit_on_change_priority), + obj); + gtk_action_group_add_radio_actions (action_group, + modest_msg_edit_file_format_action_entries, + G_N_ELEMENTS (modest_msg_edit_file_format_action_entries), + modest_conf_get_bool (modest_runtime_get_conf (), MODEST_CONF_PREFER_FORMATTED_TEXT, NULL), + G_CALLBACK (modest_ui_actions_msg_edit_on_change_file_format), + obj); + gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0); + g_object_unref (action_group); + + /* Load the UI definition */ + gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-edit-window-ui.xml", + &error); + if (error != NULL) { + g_warning ("Could not merge modest-msg-edit-window-ui.xml: %s", error->message); + g_clear_error (&error); + } + + /* Add accelerators */ + gtk_window_add_accel_group (GTK_WINDOW (obj), + gtk_ui_manager_get_accel_group (parent_priv->ui_manager)); + + parent_priv->menubar = NULL; + + size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); + + /* Note: This ModestPairList* must exist for as long as the combo + * that uses it, because the ModestComboBox uses the ID opaquely, + * so it can't know how to manage its memory. */ + priv->from_field = modest_combo_box_new (NULL, g_str_equal); + + priv->to_field = modest_recpt_editor_new (); + priv->cc_field = modest_recpt_editor_new (); + priv->bcc_field = modest_recpt_editor_new (); + subject_box = gtk_hbox_new (FALSE, 0); + priv->priority_icon = gtk_image_new (); + gtk_box_pack_start (GTK_BOX (subject_box), priv->priority_icon, FALSE, FALSE, 0); + priv->subject_field = gtk_entry_new_with_max_length (SUBJECT_MAX_LENGTH); + g_object_set (G_OBJECT (priv->subject_field), "truncate-multiline", TRUE, NULL); + hildon_gtk_entry_set_input_mode (GTK_ENTRY (priv->subject_field), + HILDON_GTK_INPUT_MODE_FULL | HILDON_GTK_INPUT_MODE_AUTOCAP); + gtk_box_pack_start (GTK_BOX (subject_box), priv->subject_field, TRUE, TRUE, 0); + priv->add_attachment_button = gtk_button_new (); + GTK_WIDGET_UNSET_FLAGS (GTK_WIDGET (priv->add_attachment_button), GTK_CAN_FOCUS); + gtk_button_set_relief (GTK_BUTTON (priv->add_attachment_button), GTK_RELIEF_NONE); + gtk_button_set_focus_on_click (GTK_BUTTON (priv->add_attachment_button), FALSE); + gtk_button_set_alignment (GTK_BUTTON (priv->add_attachment_button), 1.0, 1.0); + attachment_icon = gtk_image_new_from_icon_name (MODEST_HEADER_ICON_ATTACH, GTK_ICON_SIZE_BUTTON); + gtk_container_add (GTK_CONTAINER (priv->add_attachment_button), attachment_icon); + gtk_box_pack_start (GTK_BOX (subject_box), priv->add_attachment_button, FALSE, FALSE, 0); + priv->attachments_view = modest_attachments_view_new (NULL); + + priv->header_box = gtk_vbox_new (FALSE, 0); + + from_caption = hildon_caption_new (size_group, _("mail_va_from"), priv->from_field, NULL, 0); + to_caption = hildon_caption_new (size_group, _("mail_va_to"), priv->to_field, NULL, 0); + priv->cc_caption = hildon_caption_new (size_group, _("mail_va_cc"), priv->cc_field, NULL, 0); + priv->bcc_caption = hildon_caption_new (size_group, _("mail_va_hotfix1"), priv->bcc_field, NULL, 0); + subject_caption = hildon_caption_new (size_group, _("mail_va_subject"), subject_box, NULL, 0); + priv->attachments_caption = hildon_caption_new (size_group, _("mail_va_attachment"), priv->attachments_view, NULL, 0); + g_object_unref (size_group); + + size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); + modest_recpt_editor_set_field_size_group (MODEST_RECPT_EDITOR (priv->to_field), size_group); + modest_recpt_editor_set_field_size_group (MODEST_RECPT_EDITOR (priv->cc_field), size_group); + modest_recpt_editor_set_field_size_group (MODEST_RECPT_EDITOR (priv->bcc_field), size_group); + gtk_size_group_add_widget (size_group, priv->subject_field); + gtk_size_group_add_widget (size_group, priv->attachments_view); + g_object_unref (size_group); + + gtk_box_pack_start (GTK_BOX (priv->header_box), from_caption, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (priv->header_box), to_caption, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (priv->header_box), priv->cc_caption, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (priv->header_box), priv->bcc_caption, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (priv->header_box), subject_caption, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (priv->header_box), priv->attachments_caption, FALSE, FALSE, 0); + gtk_widget_set_no_show_all (priv->attachments_caption, TRUE); + + + priv->msg_body = wp_text_view_new (); + gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (priv->msg_body), GTK_WRAP_WORD_CHAR); + priv->text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->msg_body)); + g_object_set (priv->text_buffer, "font_scale", DEFAULT_FONT_SCALE, NULL); + wp_text_buffer_enable_rich_text (WP_TEXT_BUFFER (priv->text_buffer), TRUE); +#if (GTK_MINOR_VERSION >= 10) + gtk_text_buffer_register_serialize_tagset(GTK_TEXT_BUFFER(priv->text_buffer), NULL); + deserialize_type = gtk_text_buffer_register_deserialize_tagset(GTK_TEXT_BUFFER(priv->text_buffer), + NULL); + gtk_text_buffer_deserialize_set_can_create_tags (GTK_TEXT_BUFFER (priv->text_buffer), + deserialize_type, TRUE); +#endif + wp_text_buffer_reset_buffer (WP_TEXT_BUFFER (priv->text_buffer), TRUE); + + priv->find_toolbar = hildon_find_toolbar_new (NULL); + gtk_widget_set_no_show_all (priv->find_toolbar, TRUE); + +/* g_signal_connect (G_OBJECT (obj), "key_pressed", G_CALLBACK (on_key_pressed), NULL) */ + + priv->scroll = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->scroll), GTK_SHADOW_NONE); + modest_maemo_set_thumbable_scrollbar (GTK_SCROLLED_WINDOW(priv->scroll), TRUE); + + main_vbox = gtk_vbox_new (FALSE, DEFAULT_MAIN_VBOX_SPACING); + + gtk_box_pack_start (GTK_BOX(main_vbox), priv->header_box, FALSE, FALSE, 0); + priv->frame = gtk_frame_new (NULL); + gtk_box_pack_start (GTK_BOX(main_vbox), priv->frame, TRUE, TRUE, 0); + + gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (priv->scroll), main_vbox); + gtk_container_set_focus_vadjustment (GTK_CONTAINER (main_vbox), gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (priv->scroll))); + gtk_widget_show_all (GTK_WIDGET(priv->scroll)); + + window_box = gtk_vbox_new (FALSE, 0); + gtk_container_add (GTK_CONTAINER(obj), window_box); + + gtk_box_pack_start (GTK_BOX (window_box), priv->scroll, TRUE, TRUE, 0); + + gtk_container_add (GTK_CONTAINER (priv->frame), priv->msg_body); + + /* Set window icon */ + window_icon = modest_platform_get_icon (MODEST_APP_MSG_EDIT_ICON, MODEST_ICON_SIZE_BIG); + if (window_icon) { + gtk_window_set_icon (GTK_WINDOW (obj), window_icon); + g_object_unref (window_icon); + } +} + +static void +modest_msg_edit_window_disconnect_signals (ModestWindow *window) +{ + ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window); + + if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) && + g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY), + priv->clipboard_change_handler_id)) + g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY), + priv->clipboard_change_handler_id); + if (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD) && + g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD), + priv->default_clipboard_change_handler_id)) + g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD), + priv->default_clipboard_change_handler_id); + + if (priv->account_removed_handler_id && + g_signal_handler_is_connected (modest_runtime_get_account_store (), + priv->account_removed_handler_id)) + g_signal_handler_disconnect(modest_runtime_get_account_store (), + priv->account_removed_handler_id); +} + +static void +modest_msg_edit_window_finalize (GObject *obj) +{ + ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (obj); + + /* Sanity check: shouldn't be needed, the window mgr should + call this function before */ + modest_msg_edit_window_disconnect_signals (MODEST_WINDOW (obj)); + + if (priv->font_dialog != NULL) { + gtk_dialog_response (GTK_DIALOG (priv->font_dialog), GTK_RESPONSE_NONE); + } + + if (priv->clipboard_text != NULL) { + g_free (priv->clipboard_text); + priv->clipboard_text = NULL; + } + + if (priv->draft_msg != NULL) { + TnyHeader *header = tny_msg_get_header (priv->draft_msg); + if (TNY_IS_HEADER (header)) { + ModestWindowMgr *mgr = modest_runtime_get_window_mgr (); + modest_window_mgr_unregister_header (mgr, header); + } + g_object_unref (priv->draft_msg); + priv->draft_msg = NULL; + } + if (priv->outbox_msg != NULL) { + TnyHeader *header = tny_msg_get_header (priv->outbox_msg); + if (TNY_IS_HEADER (header)) { + ModestWindowMgr *mgr = modest_runtime_get_window_mgr (); + modest_window_mgr_unregister_header (mgr, header); + } + g_object_unref (priv->outbox_msg); + priv->outbox_msg = NULL; + } + if (priv->scroll_drag_timeout_id > 0) { + g_source_remove (priv->scroll_drag_timeout_id); + priv->scroll_drag_timeout_id = 0; + } + if (priv->clipboard_owner_idle > 0) { + g_source_remove (priv->clipboard_owner_idle); + priv->clipboard_owner_idle = 0; + } + if (priv->original_account_name) + g_free (priv->original_account_name); + g_free (priv->msg_uid); + g_free (priv->last_search); + g_slist_free (priv->font_items_group); + g_slist_free (priv->size_items_group); + g_object_unref (priv->attachments); + g_object_unref (priv->images); + + /* This had to stay alive for as long as the combobox that used it: */ + modest_pair_list_free (priv->from_field_protos); + + G_OBJECT_CLASS(parent_class)->finalize (obj); +} + +static GdkPixbuf * +pixbuf_from_stream (TnyStream *stream, const gchar *mime_type, guint64 *stream_size) +{ + GdkPixbufLoader *loader; + GdkPixbuf *pixbuf; + guint64 size; + + size = 0; + + loader = gdk_pixbuf_loader_new_with_mime_type (mime_type, NULL); + + if (loader == NULL) { + if (stream_size) + *stream_size = 0; + return NULL; + } + + tny_stream_reset (TNY_STREAM (stream)); + while (!tny_stream_is_eos (TNY_STREAM (stream))) { + GError *error = NULL; + unsigned char read_buffer[128]; + gint readed; + readed = tny_stream_read (TNY_STREAM (stream), (char *) read_buffer, 128); + size += readed; + if (!gdk_pixbuf_loader_write (loader, read_buffer, readed, &error)) { + break; + } + } + + pixbuf = gdk_pixbuf_loader_get_pixbuf (loader); + if (pixbuf) + g_object_ref (pixbuf); + gdk_pixbuf_loader_close (loader, NULL); + g_object_unref (loader); + + if (!pixbuf) + return NULL; + + if (gdk_pixbuf_get_width (pixbuf) > IMAGE_MAX_WIDTH) { + GdkPixbuf *new_pixbuf; + gint new_height; + new_height = (gdk_pixbuf_get_height (pixbuf) * IMAGE_MAX_WIDTH) / + gdk_pixbuf_get_width (pixbuf); + new_pixbuf = gdk_pixbuf_scale_simple (pixbuf, IMAGE_MAX_WIDTH, new_height, GDK_INTERP_BILINEAR); + g_object_unref (pixbuf); + pixbuf = new_pixbuf; + } + + if (stream_size) + *stream_size = size; + + return pixbuf; +} + +static void +replace_with_images (ModestMsgEditWindow *self, TnyList *attachments) +{ + ModestMsgEditWindowPrivate *priv; + TnyIterator *iter; + + priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self); + + for (iter = tny_list_create_iterator (attachments); + !tny_iterator_is_done (iter); + tny_iterator_next (iter)) { + TnyMimePart *part = (TnyMimePart *) tny_iterator_get_current (iter); + const gchar *cid = tny_mime_part_get_content_id (part); + const gchar *mime_type = tny_mime_part_get_content_type (part); + if ((cid != NULL)&&(mime_type != NULL)) { + guint64 stream_size; + TnyStream *stream = tny_mime_part_get_decoded_stream (part); + GdkPixbuf *pixbuf = pixbuf_from_stream (stream, mime_type, &stream_size); + + + g_object_unref (stream); + + if (pixbuf != NULL) { + priv->images_count ++; + priv->images_size += stream_size; + wp_text_buffer_replace_image (WP_TEXT_BUFFER (priv->text_buffer), cid, pixbuf); + g_object_unref (pixbuf); + } + } + g_object_unref (part); + } + g_object_unref (iter); +} + +static void +get_related_images (ModestMsgEditWindow *self, TnyMsg *msg) +{ + TnyMimePart *parent = NULL; + const gchar *content_type = NULL; + ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self); + + content_type = tny_mime_part_get_content_type (TNY_MIME_PART (msg)); + + if (content_type && !g_strcasecmp (content_type, "multipart/related")) { + parent = g_object_ref (msg); + } else if (content_type && !g_strcasecmp (content_type, "multipart/mixed")) { + TnyList *parts = TNY_LIST (tny_simple_list_new ()); + TnyIterator *iter; + + tny_mime_part_get_parts (TNY_MIME_PART (msg), parts); + iter = tny_list_create_iterator (parts); + while (!tny_iterator_is_done (iter)) { + TnyMimePart *part; + part = TNY_MIME_PART (tny_iterator_get_current (iter)); + content_type = tny_mime_part_get_content_type (part); + if (content_type && !g_strcasecmp (content_type, "multipart/related")) { + parent = part; + break; + } else { + g_object_unref (part); + } + tny_iterator_next (iter); + } + g_object_unref (iter); + g_object_unref (parts); + } + + if (parent != NULL) { + TnyList *parts = TNY_LIST (tny_simple_list_new ()); + TnyIterator *iter; + + tny_mime_part_get_parts (TNY_MIME_PART (parent), parts); + iter = tny_list_create_iterator (parts); + while (!tny_iterator_is_done (iter)) { + TnyMimePart *part; + part = TNY_MIME_PART (tny_iterator_get_current (iter)); + content_type = tny_mime_part_get_content_type (part); + if (content_type && g_str_has_prefix (content_type, "image/")) { + tny_list_prepend (priv->images, (GObject *) part); + } + g_object_unref (part); + tny_iterator_next (iter); + } + g_object_unref (iter); + g_object_unref (parts); + g_object_unref (parent); + } +} + +static void +update_next_cid (ModestMsgEditWindow *self, TnyList *attachments) +{ + TnyIterator *iter; + ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self); + + for (iter = tny_list_create_iterator (attachments) ; + !tny_iterator_is_done (iter); + tny_iterator_next (iter)) { + TnyMimePart *part = (TnyMimePart *) tny_iterator_get_current (iter); + const gchar *cid = tny_mime_part_get_content_id (part); + if (cid != NULL) { + char *invalid = NULL; + gint int_cid = strtol (cid, &invalid, 10); + if ((invalid != NULL) && (*invalid == '\0') && (int_cid >= priv->next_cid)) { + priv->next_cid = int_cid + 1; + } + } + g_object_unref (part); + } + g_object_unref (iter); +} + +static void +set_msg (ModestMsgEditWindow *self, TnyMsg *msg, gboolean preserve_is_rich) +{ + TnyHeader *header; + gchar *to, *cc, *bcc, *subject; + gchar *body; + ModestMsgEditWindowPrivate *priv; + GtkTextIter iter; + TnyHeaderFlags priority_flags; + TnyFolder *msg_folder; + gboolean is_html = FALSE; + + g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (self)); + g_return_if_fail (TNY_IS_MSG (msg)); + + priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self); + + header = tny_msg_get_header (msg); + to = tny_header_dup_to (header); + cc = tny_header_dup_cc (header); + bcc = tny_header_dup_bcc (header); + subject = tny_header_dup_subject (header); + priority_flags = tny_header_get_priority (header); + + if (to) + modest_recpt_editor_set_recipients (MODEST_RECPT_EDITOR (priv->to_field), to); + if (cc) { + modest_recpt_editor_set_recipients (MODEST_RECPT_EDITOR (priv->cc_field), cc); + gtk_widget_set_no_show_all (priv->cc_caption, FALSE); + gtk_widget_show (priv->cc_caption); + } else if (!modest_conf_get_bool (modest_runtime_get_conf (), MODEST_CONF_SHOW_CC, NULL)) { + gtk_widget_set_no_show_all (priv->cc_caption, TRUE); + gtk_widget_hide (priv->cc_caption); + } + if (bcc) { + modest_recpt_editor_set_recipients (MODEST_RECPT_EDITOR (priv->bcc_field), bcc); + gtk_widget_set_no_show_all (priv->bcc_caption, FALSE); + gtk_widget_show (priv->bcc_caption); + } else if (!modest_conf_get_bool (modest_runtime_get_conf (), MODEST_CONF_SHOW_BCC, NULL)) { + gtk_widget_set_no_show_all (priv->bcc_caption, TRUE); + gtk_widget_hide (priv->bcc_caption); + } + if (subject) + gtk_entry_set_text (GTK_ENTRY(priv->subject_field), subject); + modest_msg_edit_window_set_priority_flags (MODEST_MSG_EDIT_WINDOW(self), + priority_flags); + + update_window_title (self); + + wp_text_buffer_reset_buffer (WP_TEXT_BUFFER (priv->text_buffer), TRUE); + body = modest_tny_msg_get_body (msg, TRUE, &is_html); + + if ((body == NULL)||(body[0] == '\0')) { + g_free (body); + body = modest_text_utils_convert_to_html (""); + is_html = FALSE; + } + wp_text_buffer_load_document_begin (WP_TEXT_BUFFER (priv->text_buffer), TRUE); + wp_text_buffer_load_document_write (WP_TEXT_BUFFER (priv->text_buffer), + (gchar *) body, + strlen (body)); + wp_text_buffer_load_document_end (WP_TEXT_BUFFER (priv->text_buffer)); + g_free (body); + + /* Add attachments to the view */ + modest_attachments_view_set_message (MODEST_ATTACHMENTS_VIEW (priv->attachments_view), msg); + priv->attachments = modest_attachments_view_get_attachments (MODEST_ATTACHMENTS_VIEW (priv->attachments_view)); + if (tny_list_get_length (priv->attachments) == 0) { + gtk_widget_hide (priv->attachments_caption); + } else { + gtk_widget_set_no_show_all (priv->attachments_caption, FALSE); + gtk_widget_show_all (priv->attachments_caption); + } + get_related_images (self, msg); + update_next_cid (self, priv->attachments); + update_next_cid (self, priv->images); + replace_with_images (self, priv->images); + + if (preserve_is_rich && !is_html) { + wp_text_buffer_enable_rich_text (WP_TEXT_BUFFER (priv->text_buffer), FALSE); + /* Get the default format required from configuration */ + } else if (!preserve_is_rich && !modest_conf_get_bool (modest_runtime_get_conf (), MODEST_CONF_PREFER_FORMATTED_TEXT, NULL)) { + wp_text_buffer_enable_rich_text (WP_TEXT_BUFFER (priv->text_buffer), FALSE); + } + + /* Set the default focus depending on having already a To: field or not */ + if ((!to)||(*to == '\0')) { + modest_recpt_editor_grab_focus (MODEST_RECPT_EDITOR (priv->to_field)); + } else { + gtk_widget_grab_focus (priv->msg_body); + } + + /* TODO: lower priority, select in the From: combo to the + value that comes from msg <- not sure, should it be + allowed? */ + + DEBUG_BUFFER (WP_TEXT_BUFFER (priv->text_buffer)); + + gtk_text_buffer_get_start_iter (priv->text_buffer, &iter); + gtk_text_buffer_place_cursor (priv->text_buffer, &iter); + + modest_msg_edit_window_set_modified (self, FALSE); + + modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self)); + modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (self)); + text_buffer_can_undo (priv->text_buffer, FALSE, self); + text_buffer_can_redo (priv->text_buffer, FALSE, self); + + if (priv->msg_uid) { + g_free (priv->msg_uid); + priv->msg_uid = NULL; + } + + /* we should set a reference to the incoming message if it is a draft */ + msg_folder = tny_msg_get_folder (msg); + if (msg_folder) { + if (modest_tny_folder_is_local_folder (msg_folder)) { + TnyFolderType type = modest_tny_folder_get_local_or_mmc_folder_type (msg_folder); + if (type == TNY_FOLDER_TYPE_INVALID) + g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__); + + if (type == TNY_FOLDER_TYPE_DRAFTS) + priv->draft_msg = g_object_ref(msg); + if (type == TNY_FOLDER_TYPE_OUTBOX) + priv->outbox_msg = g_object_ref(msg); + priv->msg_uid = modest_tny_folder_get_header_unique_id (header); + } + g_object_unref (msg_folder); + } + + g_free (to); + g_free (subject); + g_free (cc); + g_free (bcc); +} + +static void +menu_tool_button_clicked_popup (GtkMenuToolButton *item, + gpointer data) +{ + GList *item_children, *node; + GtkWidget *bin_child; + + bin_child = gtk_bin_get_child (GTK_BIN(item)); + + item_children = gtk_container_get_children (GTK_CONTAINER (bin_child)); + + for (node = item_children; node != NULL; node = g_list_next (node)) { + if (GTK_IS_TOGGLE_BUTTON (node->data)) { + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (node->data), TRUE); + } + } + g_list_free (item_children); +} + +static void +menu_tool_button_dont_expand (GtkMenuToolButton *item) +{ + GtkWidget *box; + GList *item_children, *node; + + box = gtk_bin_get_child (GTK_BIN (item)); + gtk_box_set_homogeneous (GTK_BOX (box), TRUE); + item_children = gtk_container_get_children (GTK_CONTAINER (box)); + + for (node = item_children; node != NULL; node = g_list_next (node)) { + gtk_box_set_child_packing (GTK_BOX (box), GTK_WIDGET (node->data), TRUE, TRUE, 0, GTK_PACK_START); + if (GTK_IS_TOGGLE_BUTTON (node->data)) + gtk_button_set_alignment (GTK_BUTTON (node->data), 0.0, 0.5); + else if (GTK_IS_BUTTON (node->data)) + gtk_button_set_alignment (GTK_BUTTON (node->data), 1.0, 0.5); + } + g_list_free (item_children); +} + + +static void +modest_msg_edit_window_setup_toolbar (ModestMsgEditWindow *window) +{ + ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window); + ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window); + GtkWidget *placeholder; + GtkWidget *tool_item; + gint insert_index; + gchar size_text[5]; + gint size_index; + gint font_index; + GtkWidget *sizes_menu; + GtkWidget *fonts_menu; + GSList *radio_group = NULL; + GSList *node = NULL; + gchar *markup; + + /* Toolbar */ + parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar"); + hildon_window_add_toolbar (HILDON_WINDOW (window), GTK_TOOLBAR (parent_priv->toolbar)); + + /* Font color placeholder */ + placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FontColor"); + insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder)); + + /* font color */ + tool_item = GTK_WIDGET (gtk_tool_item_new ()); + priv->font_color_button = hildon_color_button_new (); + GTK_WIDGET_UNSET_FLAGS (tool_item, GTK_CAN_FOCUS); + GTK_WIDGET_UNSET_FLAGS (priv->font_color_button, GTK_CAN_FOCUS); + gtk_container_add (GTK_CONTAINER (tool_item), priv->font_color_button); + gtk_tool_item_set_expand (GTK_TOOL_ITEM (tool_item), TRUE); + gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (tool_item), TRUE); + gtk_toolbar_insert(GTK_TOOLBAR(parent_priv->toolbar), GTK_TOOL_ITEM (tool_item), insert_index); + g_signal_connect_swapped (G_OBJECT (priv->font_color_button), + "notify::color", + G_CALLBACK (modest_msg_edit_window_color_button_change), + window); + + /* Font size and face placeholder */ + placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FontAttributes"); + insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder)); + /* font_size */ + tool_item = GTK_WIDGET (gtk_menu_tool_button_new (NULL, NULL)); + priv->size_tool_button_label = gtk_label_new (NULL); + snprintf(size_text, sizeof(size_text), "%d", wp_font_size[DEFAULT_FONT_SIZE]); + markup = g_strconcat ("", + size_text,"", NULL); + gtk_label_set_markup (GTK_LABEL (priv->size_tool_button_label), markup); + g_free (markup); + gtk_tool_button_set_label_widget (GTK_TOOL_BUTTON (tool_item), priv->size_tool_button_label); + sizes_menu = gtk_menu_new (); + priv->size_items_group = NULL; + radio_group = NULL; + for (size_index = 0; size_index < WP_FONT_SIZE_COUNT; size_index++) { + GtkWidget *size_menu_item; + + snprintf(size_text, sizeof(size_text), "%d", wp_font_size[size_index]); + size_menu_item = gtk_radio_menu_item_new_with_label (radio_group, size_text); + radio_group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (size_menu_item)); + gtk_menu_shell_append (GTK_MENU_SHELL (sizes_menu), size_menu_item); + gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (size_menu_item), (wp_font_size[size_index] == 12)); + gtk_widget_show (size_menu_item); + + priv->size_items_group = g_slist_prepend (priv->size_items_group, size_menu_item); + + } + + for (node = radio_group; node != NULL; node = g_slist_next (node)) { + GtkWidget *item = (GtkWidget *) node->data; + g_signal_connect (G_OBJECT (item), "toggled", G_CALLBACK (modest_msg_edit_window_size_change), + window); + } + + priv->size_items_group = g_slist_reverse (priv->size_items_group); + gtk_menu_tool_button_set_menu (GTK_MENU_TOOL_BUTTON (tool_item), sizes_menu); + g_signal_connect (G_OBJECT (tool_item), "clicked", G_CALLBACK (menu_tool_button_clicked_popup), NULL); + gtk_toolbar_insert (GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM (tool_item), insert_index); + gtk_tool_item_set_expand (GTK_TOOL_ITEM (tool_item), TRUE); + gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (tool_item), TRUE); + menu_tool_button_dont_expand (GTK_MENU_TOOL_BUTTON (tool_item)); + priv->font_size_toolitem = tool_item; + + /* font face */ + tool_item = GTK_WIDGET (gtk_menu_tool_button_new (NULL, NULL)); + priv->font_tool_button_label = gtk_label_new (NULL); + markup = g_strconcat ("Tt", NULL); + gtk_label_set_markup (GTK_LABEL (priv->font_tool_button_label), markup); + g_free(markup); + gtk_tool_button_set_label_widget (GTK_TOOL_BUTTON (tool_item), priv->font_tool_button_label); + fonts_menu = gtk_menu_new (); + priv->font_items_group = NULL; + radio_group = NULL; + for (font_index = 0; font_index < wp_get_font_count (); font_index++) { + GtkWidget *font_menu_item; + GtkWidget *child_label; + + font_menu_item = gtk_radio_menu_item_new_with_label (radio_group, ""); + child_label = gtk_bin_get_child (GTK_BIN (font_menu_item)); + markup = g_strconcat ("", + wp_get_font_name (font_index), "", NULL); + gtk_label_set_markup (GTK_LABEL (child_label), markup); + g_free (markup); + + radio_group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (font_menu_item)); + gtk_menu_shell_append (GTK_MENU_SHELL (fonts_menu), font_menu_item); + gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (font_menu_item), (font_index == DEFAULT_FONT)); + gtk_widget_show (font_menu_item); + + priv->font_items_group = g_slist_prepend (priv->font_items_group, font_menu_item); + + } + for (node = radio_group; node != NULL; node = g_slist_next (node)) { + GtkWidget *item = (GtkWidget *) node->data; + g_signal_connect (G_OBJECT (item), "toggled", G_CALLBACK (modest_msg_edit_window_font_change), + window); + } + priv->font_items_group = g_slist_reverse (priv->font_items_group); + gtk_menu_tool_button_set_menu (GTK_MENU_TOOL_BUTTON (tool_item), fonts_menu); + g_signal_connect (G_OBJECT (tool_item), "clicked", G_CALLBACK (menu_tool_button_clicked_popup), NULL); + gtk_toolbar_insert (GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM (tool_item), insert_index); + gtk_tool_item_set_expand (GTK_TOOL_ITEM (tool_item), TRUE); + gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (tool_item), TRUE); + menu_tool_button_dont_expand (GTK_MENU_TOOL_BUTTON (tool_item)); + priv->font_face_toolitem = tool_item; + + /* Set expand and homogeneous for remaining items */ + tool_item = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarSend"); + gtk_tool_item_set_expand (GTK_TOOL_ITEM (tool_item), TRUE); + gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (tool_item), TRUE); + tool_item = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ActionsBold"); + gtk_tool_item_set_expand (GTK_TOOL_ITEM (tool_item), TRUE); + gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (tool_item), TRUE); + tool_item = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ActionsItalics"); + gtk_tool_item_set_expand (GTK_TOOL_ITEM (tool_item), TRUE); + gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (tool_item), TRUE); + + /* Explicitelly show all the toolbar (a normal gtk_widget_show + will not show the tool items added to the placeholders) */ + gtk_widget_show_all (parent_priv->toolbar); + + /* Set the no show all *after* showing all items. We do not + want the toolbar to be shown with a show all because it + could go agains the gconf setting regarding showing or not + the toolbar of the editor window */ + gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE); +} + + + +ModestWindow* +modest_msg_edit_window_new (TnyMsg *msg, const gchar *account_name, gboolean preserve_is_rich) +{ + GObject *obj; + ModestWindowPrivate *parent_priv; + ModestMsgEditWindowPrivate *priv; + ModestDimmingRulesGroup *menu_rules_group = NULL; + ModestDimmingRulesGroup *toolbar_rules_group = NULL; + ModestDimmingRulesGroup *clipboard_rules_group = NULL; + ModestWindowMgr *mgr = NULL; + + g_return_val_if_fail (msg, NULL); + g_return_val_if_fail (account_name, NULL); + + mgr = modest_runtime_get_window_mgr (); + + obj = G_OBJECT (modest_window_mgr_get_msg_edit_window (mgr)); + + priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (obj); + parent_priv = MODEST_WINDOW_GET_PRIVATE (obj); + + /* Menubar. Update the state of some toggles */ + parent_priv->menubar = modest_maemo_utils_get_manager_menubar_as_menu (parent_priv->ui_manager, "/MenuBar"); + hildon_window_set_menu (HILDON_WINDOW (obj), GTK_MENU (parent_priv->menubar)); + priv->from_field_protos = get_transports (); + modest_combo_box_set_pair_list (MODEST_COMBO_BOX (priv->from_field), priv->from_field_protos); + modest_combo_box_set_active_id (MODEST_COMBO_BOX (priv->from_field), (gpointer) account_name); + modest_msg_edit_window_setup_toolbar (MODEST_MSG_EDIT_WINDOW (obj)); + hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar)); + + /* Init window */ + connect_signals (MODEST_MSG_EDIT_WINDOW(obj)); + + restore_settings (MODEST_MSG_EDIT_WINDOW(obj)); + + modest_window_set_active_account (MODEST_WINDOW(obj), account_name); + + priv->original_account_name = (account_name) ? g_strdup (account_name) : NULL; + + parent_priv->ui_dimming_manager = modest_ui_dimming_manager_new (); + menu_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_MENU, FALSE); + toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE); + clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE); + /* Add common dimming rules */ + modest_dimming_rules_group_add_rules (menu_rules_group, + modest_msg_edit_window_menu_dimming_entries, + G_N_ELEMENTS (modest_msg_edit_window_menu_dimming_entries), + MODEST_WINDOW (obj)); + modest_dimming_rules_group_add_rules (toolbar_rules_group, + modest_msg_edit_window_toolbar_dimming_entries, + G_N_ELEMENTS (modest_msg_edit_window_toolbar_dimming_entries), + MODEST_WINDOW (obj)); + modest_dimming_rules_group_add_widget_rule (toolbar_rules_group, priv->font_color_button, + G_CALLBACK (modest_ui_dimming_rules_on_set_style), + MODEST_WINDOW (obj)); + modest_dimming_rules_group_add_widget_rule (toolbar_rules_group, priv->font_size_toolitem, + G_CALLBACK (modest_ui_dimming_rules_on_set_style), + MODEST_WINDOW (obj)); + modest_dimming_rules_group_add_widget_rule (toolbar_rules_group, priv->font_face_toolitem, + G_CALLBACK (modest_ui_dimming_rules_on_set_style), + MODEST_WINDOW (obj)); + modest_dimming_rules_group_add_rules (clipboard_rules_group, + modest_msg_edit_window_clipboard_dimming_entries, + G_N_ELEMENTS (modest_msg_edit_window_clipboard_dimming_entries), + MODEST_WINDOW (obj)); + /* Insert dimming rules group for this window */ + modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, menu_rules_group); + modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group); + modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group); + /* Checks the dimming rules */ + g_object_unref (menu_rules_group); + g_object_unref (toolbar_rules_group); + g_object_unref (clipboard_rules_group); + set_msg (MODEST_MSG_EDIT_WINDOW (obj), msg, preserve_is_rich); + + text_buffer_refresh_attributes (WP_TEXT_BUFFER (priv->text_buffer), MODEST_MSG_EDIT_WINDOW (obj)); + + modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj)); + modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj)); + modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD); + priv->update_caption_visibility = TRUE; + + modest_msg_edit_window_set_modified (MODEST_MSG_EDIT_WINDOW (obj), FALSE); + + /* Track account-removed signal, this window should be closed + in the case we're creating a mail associated to the account + that is deleted */ + priv->account_removed_handler_id = + g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()), + "account_removed", + G_CALLBACK(on_account_removed), + obj); + + modest_msg_edit_window_clipboard_owner_handle_change_in_idle (MODEST_MSG_EDIT_WINDOW (obj)); + + return (ModestWindow*) obj; +} + +static gint +get_formatted_data_cb (const gchar *buffer, gpointer user_data) +{ + GString **string_buffer = (GString **) user_data; + + *string_buffer = g_string_append (*string_buffer, buffer); + + return 0; +} + +/** + * @result: A new string which should be freed with g_free(). + */ +static gchar * +get_formatted_data (ModestMsgEditWindow *edit_window) +{ + ModestMsgEditWindowPrivate *priv; + GString *string_buffer = g_string_new (""); + + priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (edit_window); + + wp_text_buffer_save_document (WP_TEXT_BUFFER(priv->text_buffer), get_formatted_data_cb, &string_buffer); + + modest_text_utils_hyperlinkify (string_buffer); + + gtk_text_buffer_set_modified (priv->text_buffer, TRUE); + + return g_string_free (string_buffer, FALSE); + +} + +MsgData * +modest_msg_edit_window_get_msg_data (ModestMsgEditWindow *edit_window) +{ + MsgData *data; + const gchar *account_name; + ModestMsgEditWindowPrivate *priv; + TnyIterator *att_iter; + + g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (edit_window), NULL); + + priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (edit_window); + + account_name = modest_combo_box_get_active_id (MODEST_COMBO_BOX (priv->from_field)); + g_return_val_if_fail (account_name, NULL); + + + /* don't free these (except from) */ + data = g_slice_new0 (MsgData); + data->from = modest_account_mgr_get_from_string (modest_runtime_get_account_mgr(), + account_name); + data->account_name = g_strdup (account_name); + data->to = g_strdup (modest_recpt_editor_get_recipients (MODEST_RECPT_EDITOR (priv->to_field))); + data->cc = g_strdup (modest_recpt_editor_get_recipients (MODEST_RECPT_EDITOR (priv->cc_field))); + data->bcc = g_strdup (modest_recpt_editor_get_recipients (MODEST_RECPT_EDITOR (priv->bcc_field))); + data->subject = g_strdup (gtk_entry_get_text (GTK_ENTRY (priv->subject_field))); + if (priv->draft_msg) { + data->draft_msg = g_object_ref (priv->draft_msg); + } else if (priv->outbox_msg) { + data->draft_msg = g_object_ref (priv->outbox_msg); + } else { + data->draft_msg = NULL; + } + + GtkTextBuffer *buf = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->msg_body)); + GtkTextIter b, e; + gtk_text_buffer_get_bounds (buf, &b, &e); + data->plain_body = modest_text_utils_text_buffer_get_text (priv->text_buffer); /* returns a copy */ + + if (wp_text_buffer_is_rich_text (WP_TEXT_BUFFER (priv->text_buffer))) + data->html_body = get_formatted_data (edit_window); /* returns a copy. */ + else + data->html_body = NULL; + + /* deep-copy the data */ + att_iter = tny_list_create_iterator (priv->attachments); + data->attachments = NULL; + while (!tny_iterator_is_done (att_iter)) { + TnyMimePart *part = (TnyMimePart *) tny_iterator_get_current (att_iter); + if (!(TNY_IS_MIME_PART(part))) { + g_warning ("strange data in attachment list"); + g_object_unref (part); + tny_iterator_next (att_iter); + continue; + } + data->attachments = g_list_append (data->attachments, + part); + tny_iterator_next (att_iter); + } + g_object_unref (att_iter); + + GtkTextTagTable *tag_table = gtk_text_buffer_get_tag_table (GTK_TEXT_BUFFER (priv->text_buffer)); + att_iter = tny_list_create_iterator (priv->images); + data->images = NULL; + while (!tny_iterator_is_done (att_iter)) { + TnyMimePart *part = (TnyMimePart *) tny_iterator_get_current (att_iter); + const gchar *cid; + if (!(TNY_IS_MIME_PART(part))) { + g_warning ("strange data in attachment list"); + g_object_unref (part); + tny_iterator_next (att_iter); + continue; + } + cid = tny_mime_part_get_content_id (part); + if (cid) { + gchar *image_tag_id; + GtkTextTag *image_tag; + GtkTextIter iter; + image_tag_id = g_strdup_printf ("image-tag-%s", cid); + image_tag = gtk_text_tag_table_lookup (tag_table, image_tag_id); + g_free (image_tag_id); + + gtk_text_buffer_get_start_iter (priv->text_buffer, &iter); + if (image_tag && + ((gtk_text_iter_has_tag (&iter, image_tag))|| + (gtk_text_iter_forward_to_tag_toggle (&iter, image_tag)))) + data->images = g_list_append (data->images, + g_object_ref (part)); + } + g_object_unref (part); + tny_iterator_next (att_iter); + } + g_object_unref (att_iter); + + data->priority_flags = priv->priority_flags; + + return data; +} + + +static void +unref_gobject (GObject *obj, gpointer data) +{ + if (!G_IS_OBJECT(obj)) + return; + g_object_unref (obj); +} + +void +modest_msg_edit_window_free_msg_data (ModestMsgEditWindow *edit_window, + MsgData *data) +{ + g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (edit_window)); + + if (!data) + return; + + g_free (data->to); + g_free (data->cc); + g_free (data->bcc); + g_free (data->from); + g_free (data->subject); + g_free (data->plain_body); + g_free (data->html_body); + g_free (data->account_name); + + if (data->draft_msg != NULL) { + g_object_unref (data->draft_msg); + data->draft_msg = NULL; + } + + g_list_foreach (data->attachments, (GFunc)unref_gobject, NULL); + g_list_free (data->attachments); + g_list_foreach (data->images, (GFunc)unref_gobject, NULL); + g_list_free (data->images); + + g_slice_free (MsgData, data); +} + +void +modest_msg_edit_window_get_parts_size (ModestMsgEditWindow *window, + gint *parts_count, + guint64 *parts_size) +{ + ModestMsgEditWindowPrivate *priv; + + priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window); + + modest_attachments_view_get_sizes (MODEST_ATTACHMENTS_VIEW (priv->attachments_view), parts_count, parts_size); + + /* TODO: add images */ + *parts_size += priv->images_size; + *parts_count += priv->images_count; + +} + +ModestMsgEditFormat +modest_msg_edit_window_get_format (ModestMsgEditWindow *self) +{ + gboolean rich_text; + ModestMsgEditWindowPrivate *priv = NULL; + g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (self), MODEST_MSG_EDIT_FORMAT_HTML); + + priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self); + + rich_text = wp_text_buffer_is_rich_text (WP_TEXT_BUFFER (priv->text_buffer)); + if (rich_text) + return MODEST_MSG_EDIT_FORMAT_HTML; + else + return MODEST_MSG_EDIT_FORMAT_TEXT; +} + +void +modest_msg_edit_window_set_format (ModestMsgEditWindow *self, + ModestMsgEditFormat format) +{ + ModestMsgEditWindowPrivate *priv; + + g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (self)); + priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self); + + switch (format) { + case MODEST_MSG_EDIT_FORMAT_HTML: + wp_text_buffer_enable_rich_text (WP_TEXT_BUFFER (priv->text_buffer), TRUE); + break; + case MODEST_MSG_EDIT_FORMAT_TEXT: + wp_text_buffer_enable_rich_text (WP_TEXT_BUFFER (priv->text_buffer), FALSE); + break; + default: + g_return_if_reached (); + } +} + +ModestMsgEditFormatState * +modest_msg_edit_window_get_format_state (ModestMsgEditWindow *self) +{ + ModestMsgEditFormatState *format_state = NULL; + ModestMsgEditWindowPrivate *priv; + WPTextBufferFormat *buffer_format = g_new0 (WPTextBufferFormat, 1); + + g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (self), NULL); + priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self); + + wp_text_buffer_get_attributes (WP_TEXT_BUFFER (priv->text_buffer), buffer_format, TRUE); + + format_state = g_new0 (ModestMsgEditFormatState, 1); + format_state->bold = buffer_format->bold&0x1; + format_state->italics = buffer_format->italic&0x1; + format_state->bullet = buffer_format->bullet&0x1; + format_state->color = buffer_format->color; + format_state->font_size = buffer_format->font_size; + format_state->font_family = wp_get_font_name (buffer_format->font); + format_state->justification = buffer_format->justification; + g_free (buffer_format); + + return format_state; + +} + +void +modest_msg_edit_window_set_format_state (ModestMsgEditWindow *self, + const ModestMsgEditFormatState *format_state) +{ + ModestMsgEditWindowPrivate *priv; + WPTextBufferFormat *buffer_format = g_new0 (WPTextBufferFormat, 1); + WPTextBufferFormat *current_format = g_new0 (WPTextBufferFormat, 1); + g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (self)); + g_return_if_fail (format_state != NULL); + + priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self); + gtk_widget_grab_focus (priv->msg_body); + buffer_format->bold = (format_state->bold != FALSE); + buffer_format->italic = (format_state->italics != FALSE); + buffer_format->color = format_state->color; + buffer_format->font_size = format_state->font_size; + buffer_format->font = wp_get_font_index (format_state->font_family, 0); + buffer_format->justification = format_state->justification; + buffer_format->bullet = format_state->bullet; + + wp_text_buffer_get_attributes (WP_TEXT_BUFFER (priv->text_buffer), current_format, TRUE); + + buffer_format->cs.bold = ((buffer_format->bold&0x1) != (current_format->bold&0x1)); + buffer_format->cs.italic = ((buffer_format->italic&0x1) != (current_format->italic&0x1)); + buffer_format->cs.color = !gdk_color_equal(&(buffer_format->color), &(current_format->color)); + buffer_format->cs.font_size = (buffer_format->font_size != current_format->font_size); + buffer_format->cs.font = (buffer_format->font != current_format->font); + buffer_format->cs.justification = (buffer_format->justification != current_format->justification); + buffer_format->cs.bullet = (buffer_format->bullet != current_format->bullet); + + wp_text_buffer_freeze (WP_TEXT_BUFFER (priv->text_buffer)); + if (buffer_format->cs.bold) { + wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_BOLD, + GINT_TO_POINTER (buffer_format->bold&0x1)); + } + if (buffer_format->cs.italic) { + wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_ITALIC, + GINT_TO_POINTER (buffer_format->italic&0x1)); + } + if (buffer_format->cs.color) { + wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FORECOLOR, + GINT_TO_POINTER (&(buffer_format->color))); + } + if (buffer_format->cs.font_size) { + wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FONT_SIZE, + GINT_TO_POINTER (buffer_format->font_size)); + } + if (buffer_format->cs.justification) { + switch (buffer_format->justification) { + case GTK_JUSTIFY_LEFT: + wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_LEFT, + GINT_TO_POINTER(TRUE)); + break; + case GTK_JUSTIFY_CENTER: + wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_CENTER, + GINT_TO_POINTER(TRUE)); + break; + case GTK_JUSTIFY_RIGHT: + wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_RIGHT, + GINT_TO_POINTER(TRUE)); + break; + default: + break; + } + + } + if (buffer_format->cs.font) { + wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FONT, + GINT_TO_POINTER (buffer_format->font)); + } + wp_text_buffer_thaw (WP_TEXT_BUFFER (priv->text_buffer)); + if (buffer_format->cs.bullet) { + wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_BULLET, + GINT_TO_POINTER ((buffer_format->bullet)?1:0)); + } +/* wp_text_buffer_set_format (WP_TEXT_BUFFER (priv->text_buffer), buffer_format); */ + + text_buffer_refresh_attributes (WP_TEXT_BUFFER (priv->text_buffer), self); + + g_free (current_format); + + /* Check dimming rules */ + modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self)); + modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (self)); +} + +static void +text_buffer_refresh_attributes (WPTextBuffer *buffer, ModestMsgEditWindow *window) +{ + WPTextBufferFormat *buffer_format = g_new0 (WPTextBufferFormat, 1); + GtkAction *action; + ModestWindowPrivate *parent_priv; + ModestMsgEditWindowPrivate *priv; + GtkWidget *new_size_menuitem; + GtkWidget *new_font_menuitem; + + parent_priv = MODEST_WINDOW_GET_PRIVATE (window); + priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window); + + if (wp_text_buffer_is_rich_text (WP_TEXT_BUFFER (priv->text_buffer))) { + action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/FileFormatMenu/FileFormatFormattedTextMenu"); + if (!gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) + modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), TRUE); + } else { + action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/FileFormatMenu/FileFormatPlainTextMenu"); + if (!gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) + modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), TRUE); + } + + wp_text_buffer_get_attributes (WP_TEXT_BUFFER (priv->text_buffer), buffer_format, FALSE); + + action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ActionsBold"); + modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), buffer_format->bold); + + action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ActionsItalics"); + modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), buffer_format->italic); + +/* action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/BulletedListMenu"); */ +/* modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), buffer_format->bullet); */ + + action = NULL; + switch (buffer_format->justification) + { + case GTK_JUSTIFY_LEFT: + action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/AlignmentMenu/AlignmentLeftMenu"); + break; + case GTK_JUSTIFY_CENTER: + action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/AlignmentMenu/AlignmentCenterMenu"); + break; + case GTK_JUSTIFY_RIGHT: + action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/AlignmentMenu/AlignmentRightMenu"); + break; + default: + break; + } + + if (action != NULL) + gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE); + + g_signal_handlers_block_by_func (G_OBJECT (priv->font_color_button), + G_CALLBACK (modest_msg_edit_window_color_button_change), + window); + hildon_color_button_set_color (HILDON_COLOR_BUTTON (priv->font_color_button), & (buffer_format->color)); + g_signal_handlers_unblock_by_func (G_OBJECT (priv->font_color_button), + G_CALLBACK (modest_msg_edit_window_color_button_change), + window); + + new_size_menuitem = GTK_WIDGET ((g_slist_nth (priv->size_items_group, + buffer_format->font_size))->data); + if (!gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (new_size_menuitem))) { + GtkWidget *label; + gchar *markup; + + label = gtk_bin_get_child (GTK_BIN (new_size_menuitem)); + markup = g_strconcat ("", gtk_label_get_text (GTK_LABEL (label)), "", NULL); + gtk_label_set_markup (GTK_LABEL (priv->size_tool_button_label), markup); + g_free (markup); + g_signal_handlers_block_by_func (G_OBJECT (new_size_menuitem), + G_CALLBACK (modest_msg_edit_window_size_change), + window); + gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (new_size_menuitem), TRUE); + g_signal_handlers_unblock_by_func (G_OBJECT (new_size_menuitem), + G_CALLBACK (modest_msg_edit_window_size_change), + window); + } + + new_font_menuitem = GTK_WIDGET ((g_slist_nth (priv->font_items_group, + buffer_format->font))->data); + if (!gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (new_font_menuitem))) { + GtkWidget *label; + gchar *markup; + + label = gtk_bin_get_child (GTK_BIN (new_font_menuitem)); + markup = g_strconcat ("Tt", NULL); + gtk_label_set_markup (GTK_LABEL (priv->font_tool_button_label), markup); + g_free (markup); + g_signal_handlers_block_by_func (G_OBJECT (new_font_menuitem), + G_CALLBACK (modest_msg_edit_window_font_change), + window); + gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (new_font_menuitem), TRUE); + g_signal_handlers_unblock_by_func (G_OBJECT (new_font_menuitem), + G_CALLBACK (modest_msg_edit_window_font_change), + window); + } + + g_free (buffer_format); + +} + +#ifdef MODEST_HILDON_VERSION_0 +void +modest_msg_edit_window_select_color (ModestMsgEditWindow *window) +{ + + WPTextBufferFormat *buffer_format = g_new0 (WPTextBufferFormat, 1); + ModestMsgEditWindowPrivate *priv; + GtkWidget *dialog = NULL; + gint response; + GdkColor *new_color = NULL; + + priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window); + wp_text_buffer_get_attributes (WP_TEXT_BUFFER (priv->text_buffer), buffer_format, FALSE); + + dialog = hildon_color_selector_new (GTK_WINDOW (window)); + hildon_color_selector_set_color (HILDON_COLOR_SELECTOR (dialog), &(buffer_format->color)); + g_free (buffer_format); + + if (gtk_dialog_run (GTK_DIALOG (dialog) == GTK_RESPONSE_OK)) { + new_color = hildon_color_selector_get_color (HILDON_COLOR_SELECTOR (dialog)); + if (new_color != NULL) { + wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FORECOLOR, + (gpointer) new_color); + } + } + gtk_widget_destroy (dialog); +} + + +void +modest_msg_edit_window_select_background_color (ModestMsgEditWindow *window) +{ + + ModestMsgEditWindowPrivate *priv; + GtkWidget *dialog = NULL; + gint response; + GdkColor *old_color = NULL; + const GdkColor *new_color = NULL; + + priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window); + old_color = (GdkColor*)wp_text_buffer_get_background_color (WP_TEXT_BUFFER (priv->text_buffer)); + + dialog = hildon_color_selector_new (GTK_WINDOW (window)); + hildon_color_selector_set_color (HILDON_COLOR_SELECTOR (dialog),(GdkColor*)old_color); + + if (gtk_dialog_run (GTK_DIALOG (dialog) == GTK_RESPONSE_OK)) { + new_color = hildon_color_selector_get_color (HILDON_COLOR_SELECTOR (dialog)); + if (new_color != NULL) + wp_text_buffer_set_background_color (WP_TEXT_BUFFER (priv->text_buffer), new_color); + } + gtk_widget_destroy (dialog); + +} + +#else +void +modest_msg_edit_window_select_color (ModestMsgEditWindow *window) +{ + + WPTextBufferFormat *buffer_format = g_new0 (WPTextBufferFormat, 1); + ModestMsgEditWindowPrivate *priv; + GtkWidget *dialog = NULL; + + priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window); + wp_text_buffer_get_attributes (WP_TEXT_BUFFER (priv->text_buffer), buffer_format, FALSE); + + dialog = hildon_color_chooser_new (); + hildon_color_chooser_set_color (HILDON_COLOR_CHOOSER (dialog), &(buffer_format->color)); + g_free (buffer_format); + + if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) { + GdkColor col; + hildon_color_chooser_get_color (HILDON_COLOR_CHOOSER(dialog), &col); + wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FORECOLOR, + (gpointer) &col); + } + gtk_widget_destroy (dialog); +} + + +void +modest_msg_edit_window_select_background_color (ModestMsgEditWindow *window) +{ + + ModestMsgEditWindowPrivate *priv; + GtkWidget *dialog = NULL; + GdkColor *old_color = NULL; + + priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window); + old_color = (GdkColor*)wp_text_buffer_get_background_color (WP_TEXT_BUFFER (priv->text_buffer)); + + dialog = hildon_color_chooser_new (); + hildon_color_chooser_set_color (HILDON_COLOR_CHOOSER (dialog),(GdkColor*)old_color); + + if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) { + GdkColor col; + hildon_color_chooser_get_color (HILDON_COLOR_CHOOSER(dialog), &col); + wp_text_buffer_set_background_color (WP_TEXT_BUFFER (priv->text_buffer), &col); + } + gtk_widget_destroy (dialog); +} + +#endif /*!MODEST_HILDON_VERSION_0*/ + + + +static TnyStream* +create_stream_for_uri (const gchar* uri) +{ + if (!uri) + return NULL; + + TnyStream *result = NULL; + + GnomeVFSHandle *handle = NULL; + GnomeVFSResult test = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ); + if (test == GNOME_VFS_OK) { + TnyStream *vfssstream = TNY_STREAM (tny_vfs_stream_new (handle)); + /* Streams over OBEX (Bluetooth) are not seekable but + * we expect them to be (we might need to read them + * several times). So if this is a Bluetooth URI just + * read the whole file into memory (this is not a fast + * protocol so we can assume that these files are not + * going to be very big) */ + if ((g_ascii_strncasecmp (uri, "obex://", 7) == 0)|| + (g_ascii_strncasecmp (uri, "upnpav://", 9) == 0)) { + TnyStream *memstream = tny_camel_mem_stream_new (); + tny_stream_write_to_stream (vfssstream, memstream); + g_object_unref (vfssstream); + result = memstream; + } else { + result = vfssstream; + } + } + + return result; +} + +void +modest_msg_edit_window_insert_image (ModestMsgEditWindow *window) +{ + + ModestMsgEditWindowPrivate *priv; + GtkWidget *dialog = NULL; + gint response = 0; + GSList *uris = NULL; + GSList *uri_node = NULL; + + priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window); + + dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window), GTK_FILE_CHOOSER_ACTION_OPEN); + gtk_window_set_title (GTK_WINDOW (dialog), _("mcen_ia_select_inline_image_title")); + gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (dialog), TRUE); + + modest_maemo_utils_setup_images_filechooser (GTK_FILE_CHOOSER (dialog)); + + modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), + GTK_WINDOW (dialog)); + + response = gtk_dialog_run (GTK_DIALOG (dialog)); + switch (response) { + case GTK_RESPONSE_OK: + uris = gtk_file_chooser_get_uris (GTK_FILE_CHOOSER (dialog)); + break; + default: + break; + } + gtk_widget_destroy (dialog); + + for (uri_node = uris; uri_node != NULL; uri_node = g_slist_next (uri_node)) { + const gchar *uri; + GnomeVFSHandle *handle = NULL; + GnomeVFSResult result; + GtkTextIter position; + GtkTextMark *insert_mark; + + uri = (const gchar *) uri_node->data; + result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ); + if (result == GNOME_VFS_OK) { + GdkPixbuf *pixbuf; + GnomeVFSFileInfo *info; + gchar *filename, *basename, *escaped_filename; + TnyMimePart *mime_part; + gchar *content_id; + const gchar *mime_type = NULL; + GnomeVFSURI *vfs_uri; + guint64 stream_size; + + gnome_vfs_close (handle); + vfs_uri = gnome_vfs_uri_new (uri); + + escaped_filename = g_path_get_basename (gnome_vfs_uri_get_path (vfs_uri)); + filename = gnome_vfs_unescape_string_for_display (escaped_filename); + g_free (escaped_filename); + gnome_vfs_uri_unref (vfs_uri); + info = gnome_vfs_file_info_new (); + + if (gnome_vfs_get_file_info (uri, info, GNOME_VFS_FILE_INFO_GET_MIME_TYPE + | GNOME_VFS_FILE_INFO_FORCE_SLOW_MIME_TYPE) + == GNOME_VFS_OK) + mime_type = gnome_vfs_file_info_get_mime_type (info); + + mime_part = tny_platform_factory_new_mime_part + (modest_runtime_get_platform_factory ()); + + TnyStream *stream = create_stream_for_uri (uri); + + if (stream == NULL) { + + modest_platform_information_banner (NULL, NULL, dgettext("hildon-fm", "sfil_ib_opening_not_allowed")); + + g_object_unref (mime_part); + gnome_vfs_file_info_unref (info); + continue; + } + + tny_mime_part_construct (mime_part, stream, mime_type, "base64"); + + content_id = g_strdup_printf ("%d", priv->next_cid); + tny_mime_part_set_content_id (mime_part, content_id); + g_free (content_id); + priv->next_cid++; + + basename = g_path_get_basename (filename); + tny_mime_part_set_filename (mime_part, basename); + g_free (basename); + + pixbuf = pixbuf_from_stream (stream, mime_type, &stream_size); + + if (pixbuf != NULL) { + priv->images_size += stream_size; + priv->images_count ++; + insert_mark = gtk_text_buffer_get_insert (GTK_TEXT_BUFFER (priv->text_buffer)); + gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (priv->text_buffer), &position, insert_mark); + wp_text_buffer_insert_image (WP_TEXT_BUFFER (priv->text_buffer), &position, g_strdup (tny_mime_part_get_content_id (mime_part)), pixbuf); + } + + tny_list_prepend (priv->images, (GObject *) mime_part); + gtk_text_buffer_set_modified (priv->text_buffer, TRUE); + g_free (filename); + g_object_unref (mime_part); + gnome_vfs_file_info_unref (info); + + } + } + + +} + +void +modest_msg_edit_window_offer_attach_file (ModestMsgEditWindow *window) +{ + GtkWidget *dialog = NULL; + gint response = 0; + GSList *uris = NULL; + GSList *uri_node; + GnomeVFSFileSize total_size, allowed_size; + ModestMsgEditWindowPrivate *priv; + gint att_num; + guint64 att_size; + + g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW(window)); + + priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window); + + if (modest_platform_check_memory_low (MODEST_WINDOW(window), TRUE)) + return; + + dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window), GTK_FILE_CHOOSER_ACTION_OPEN); + gtk_window_set_title (GTK_WINDOW (dialog), _("mcen_ti_select_attachment_title")); + gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (dialog), TRUE); + modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), GTK_WINDOW (dialog)); + + response = gtk_dialog_run (GTK_DIALOG (dialog)); + switch (response) { + case GTK_RESPONSE_OK: + uris = gtk_file_chooser_get_uris (GTK_FILE_CHOOSER (dialog)); + break; + default: + break; + } + gtk_widget_destroy (dialog); + + /* allowed size is the maximum size - what's already there */ + modest_attachments_view_get_sizes ( + MODEST_ATTACHMENTS_VIEW (priv->attachments_view), + &att_num, &att_size); + allowed_size = MODEST_MAX_ATTACHMENT_SIZE - att_size; + + total_size = 0; + for (uri_node = uris; uri_node != NULL; uri_node = g_slist_next (uri_node)) { + + const gchar *uri = (const gchar *) uri_node->data; + + total_size += modest_msg_edit_window_attach_file_one + (window, uri, allowed_size); + + if (total_size > allowed_size) { + g_warning ("%s: total size: %u", + __FUNCTION__, (unsigned int)total_size); + break; + } + + allowed_size -= total_size; + + + } + g_slist_foreach (uris, (GFunc) g_free, NULL); + g_slist_free (uris); +} + + +GnomeVFSFileSize +modest_msg_edit_window_attach_file_one (ModestMsgEditWindow *window, + const gchar *uri, + GnomeVFSFileSize allowed_size) + +{ + GnomeVFSHandle *handle = NULL; + ModestMsgEditWindowPrivate *priv; + GnomeVFSResult result; + GnomeVFSFileSize size = 0; + g_return_val_if_fail (window, 0); + g_return_val_if_fail (uri, 0); + + priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window); + + result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ); + if (result == GNOME_VFS_OK) { + TnyMimePart *mime_part; + TnyStream *stream; + const gchar *mime_type = NULL; + gchar *basename; + gchar *escaped_filename; + gchar *filename; + gchar *content_id; + GnomeVFSFileInfo *info; + GnomeVFSURI *vfs_uri; + + gnome_vfs_close (handle); + vfs_uri = gnome_vfs_uri_new (uri); + + + escaped_filename = g_path_get_basename (gnome_vfs_uri_get_path (vfs_uri)); + filename = gnome_vfs_unescape_string_for_display (escaped_filename); + g_free (escaped_filename); + gnome_vfs_uri_unref (vfs_uri); + + info = gnome_vfs_file_info_new (); + + if (gnome_vfs_get_file_info (uri, + info, + GNOME_VFS_FILE_INFO_GET_MIME_TYPE) + == GNOME_VFS_OK) + mime_type = gnome_vfs_file_info_get_mime_type (info); + mime_part = tny_platform_factory_new_mime_part + (modest_runtime_get_platform_factory ()); + + /* try to get the attachment's size; this may fail for weird + * file systems, like obex, upnp... */ + if (allowed_size != 0 && + info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE) { + size = info->size; + if (size > allowed_size) { + modest_platform_information_banner (NULL, NULL, + dgettext("hildon-fm", "sfil_ib_opening_not_allowed")); + return 0; + } + } else + g_warning ("%s: could not get attachment size", __FUNCTION__); + + stream = create_stream_for_uri (uri); + + if (stream == NULL) { + + modest_platform_information_banner (NULL, NULL, dgettext("hildon-fm", "sfil_ib_opening_not_allowed")); + + g_object_unref (mime_part); + gnome_vfs_file_info_unref (info); + return 0; + } + + tny_mime_part_construct (mime_part, stream, mime_type, "base64"); + g_object_unref (stream); + + content_id = g_strdup_printf ("%d", priv->next_cid); + tny_mime_part_set_content_id (mime_part, content_id); + g_free (content_id); + priv->next_cid++; + + basename = g_path_get_basename (filename); + tny_mime_part_set_filename (mime_part, basename); + g_free (basename); + + tny_list_prepend (priv->attachments, (GObject *) mime_part); + modest_attachments_view_add_attachment (MODEST_ATTACHMENTS_VIEW (priv->attachments_view), + mime_part, + info->size == 0, info->size); + gtk_widget_set_no_show_all (priv->attachments_caption, FALSE); + gtk_widget_show_all (priv->attachments_caption); + gtk_text_buffer_set_modified (priv->text_buffer, TRUE); + g_free (filename); + g_object_unref (mime_part); + gnome_vfs_file_info_unref (info); + } + + return size; +} + +void +modest_msg_edit_window_remove_attachments (ModestMsgEditWindow *window, + TnyList *att_list) +{ + ModestMsgEditWindowPrivate *priv; + TnyIterator *iter; + + g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window)); + priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window); + + if (att_list == NULL) { + att_list = modest_attachments_view_get_selection (MODEST_ATTACHMENTS_VIEW (priv->attachments_view)); + } else { + g_object_ref (att_list); + } + + if (tny_list_get_length (att_list) == 0) { + hildon_banner_show_information (NULL, NULL, _("TODO: no attachments selected to remove")); + } else { + gboolean dialog_response; + gchar *message = NULL; + gchar *filename = NULL; + + if (tny_list_get_length (att_list) == 1) { + TnyMimePart *part; + iter = tny_list_create_iterator (att_list); + part = (TnyMimePart *) tny_iterator_get_current (iter); + g_object_unref (iter); + if (TNY_IS_MSG (part)) { + TnyHeader *header = tny_msg_get_header (TNY_MSG (part)); + if (header) { + filename = tny_header_dup_subject (header); + g_object_unref (header); + } + if (filename == NULL) { + filename = g_strdup (_("mail_va_no_subject")); + } + } else { + filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part))); + } + g_object_unref (part); + } else { + filename = g_strdup (""); + } + message = g_strdup_printf (ngettext("emev_nc_delete_attachment", "emev_nc_delete_attachments", + tny_list_get_length (att_list)), filename); + g_free (filename); + + dialog_response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window), message); + g_free (message); + + if (dialog_response != GTK_RESPONSE_OK) { + g_object_unref (att_list); + return; + } + hildon_banner_show_information (NULL, NULL, _("mcen_ib_removing_attachment")); + + for (iter = tny_list_create_iterator (att_list); + !tny_iterator_is_done (iter); + tny_iterator_next (iter)) { + TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter); + const gchar *att_id; + tny_list_remove (priv->attachments, (GObject *) mime_part); + + modest_attachments_view_remove_attachment (MODEST_ATTACHMENTS_VIEW (priv->attachments_view), + mime_part); + if (tny_list_get_length (priv->attachments) == 0) + gtk_widget_hide (priv->attachments_caption); + att_id = tny_mime_part_get_content_id (mime_part); + if (att_id != NULL) + text_buffer_delete_images_by_id (gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->msg_body)), + att_id); + gtk_text_buffer_set_modified (priv->text_buffer, TRUE); + g_object_unref (mime_part); + } + g_object_unref (iter); + } + + g_object_unref (att_list); + + /* if the last attachment has been removed, focus the Subject: field */ + if (!modest_attachments_view_has_attachments (MODEST_ATTACHMENTS_VIEW (priv->attachments_view))) + gtk_widget_grab_focus (priv->subject_field); +} + +static void +modest_msg_edit_window_color_button_change (ModestMsgEditWindow *window, + gpointer userdata) +{ + ModestMsgEditWindowPrivate *priv; + GdkColor *new_color; + priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window); + +#ifdef MODEST_HAVE_HILDON0_WIDGETS + new_color = hildon_color_button_get_color (HILDON_COLOR_BUTTON (priv->font_color_button)); +#else + GdkColor col; + hildon_color_button_get_color (HILDON_COLOR_BUTTON(priv->font_color_button), &col); + new_color = &col; +#endif /*#ifdef MODEST_HAVE_HILDON0_WIDGETS*/ + + wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FORECOLOR, (gpointer) new_color); + + gtk_window_set_focus (GTK_WINDOW (window), priv->msg_body); + +} + +static void +modest_msg_edit_window_size_change (GtkCheckMenuItem *menu_item, + gpointer userdata) +{ + ModestMsgEditWindowPrivate *priv; + gint new_size_index; + ModestMsgEditWindow *window; + GtkWidget *label; + + window = MODEST_MSG_EDIT_WINDOW (userdata); + priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window); + gtk_widget_grab_focus (GTK_WIDGET (priv->msg_body)); + + if (gtk_check_menu_item_get_active (menu_item)) { + gchar *markup; + WPTextBufferFormat format; + + memset (&format, 0, sizeof (format)); + wp_text_buffer_get_attributes (WP_TEXT_BUFFER (priv->text_buffer), &format, FALSE); + + label = gtk_bin_get_child (GTK_BIN (menu_item)); + + new_size_index = atoi (gtk_label_get_text (GTK_LABEL (label))); + format.cs.font_size = TRUE; + format.cs.text_position = TRUE; + format.cs.font = TRUE; + format.font_size = wp_get_font_size_index (new_size_index, DEFAULT_FONT_SIZE); +/* wp_text_buffer_set_format (WP_TEXT_BUFFER (priv->text_buffer), &format); */ + + if (!wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FONT_SIZE, + GINT_TO_POINTER (wp_get_font_size_index (new_size_index, 12)))) + wp_text_view_reset_and_show_im (WP_TEXT_VIEW (priv->msg_body)); + + text_buffer_refresh_attributes (WP_TEXT_BUFFER (priv->text_buffer), MODEST_MSG_EDIT_WINDOW (window)); + markup = g_strconcat ("", gtk_label_get_text (GTK_LABEL (label)), "", NULL); + gtk_label_set_markup (GTK_LABEL (priv->size_tool_button_label), markup); + g_free (markup); + } +} + +static void +modest_msg_edit_window_font_change (GtkCheckMenuItem *menu_item, + gpointer userdata) +{ + ModestMsgEditWindowPrivate *priv; + gint new_font_index; + ModestMsgEditWindow *window; + GtkWidget *label; + + window = MODEST_MSG_EDIT_WINDOW (userdata); + priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window); + gtk_widget_grab_focus (GTK_WIDGET (priv->msg_body)); + + if (gtk_check_menu_item_get_active (menu_item)) { + gchar *markup; + + label = gtk_bin_get_child (GTK_BIN (menu_item)); + + new_font_index = wp_get_font_index (gtk_label_get_text (GTK_LABEL (label)), DEFAULT_FONT); + + if (!wp_text_buffer_set_attribute (WP_TEXT_BUFFER (priv->text_buffer), WPT_FONT, + GINT_TO_POINTER(new_font_index))) + wp_text_view_reset_and_show_im (WP_TEXT_VIEW (priv->msg_body)); + + text_buffer_refresh_attributes (WP_TEXT_BUFFER (priv->text_buffer), MODEST_MSG_EDIT_WINDOW (window)); + markup = g_strconcat ("Tt", NULL); + gtk_label_set_markup (GTK_LABEL (priv->font_tool_button_label), markup); + g_free (markup); + } +} + +static gboolean +modest_msg_edit_window_window_state_event (GtkWidget *widget, GdkEventWindowState *event, gpointer userdata) +{ + if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) { + ModestWindowPrivate *parent_priv; + ModestWindowMgr *mgr; + gboolean is_fullscreen; + GtkAction *fs_toggle_action; + gboolean active; + + mgr = modest_runtime_get_window_mgr (); + is_fullscreen = (modest_window_mgr_get_fullscreen_mode (mgr))?1:0; + + parent_priv = MODEST_WINDOW_GET_PRIVATE (widget); + + fs_toggle_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewToggleFullscreenMenu"); + active = (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (fs_toggle_action)))?1:0; + if (is_fullscreen != active) + gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (fs_toggle_action), is_fullscreen); + } + + return FALSE; + +} + +void +modest_msg_edit_window_show_cc (ModestMsgEditWindow *window, + gboolean show) +{ + ModestMsgEditWindowPrivate *priv = NULL; + g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window)); + + priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window); + if (!priv->update_caption_visibility) + return; + + gtk_widget_set_no_show_all (priv->cc_caption, TRUE); + if (show) + gtk_widget_show (priv->cc_caption); + else + gtk_widget_hide (priv->cc_caption); + + modest_conf_set_bool(modest_runtime_get_conf(), MODEST_CONF_SHOW_CC, show, NULL); +} + +void +modest_msg_edit_window_show_bcc (ModestMsgEditWindow *window, + gboolean show) +{ + ModestMsgEditWindowPrivate *priv = NULL; + g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window)); + + priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window); + if (!priv->update_caption_visibility) + return; + + gtk_widget_set_no_show_all (priv->bcc_caption, TRUE); + if (show) + gtk_widget_show (priv->bcc_caption); + else + gtk_widget_hide (priv->bcc_caption); + + modest_conf_set_bool(modest_runtime_get_conf(), MODEST_CONF_SHOW_BCC, show, NULL); +} + +static void +modest_msg_edit_window_open_addressbook (ModestMsgEditWindow *window, + ModestRecptEditor *editor) +{ + ModestMsgEditWindowPrivate *priv; + + g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window)); + g_return_if_fail ((editor == NULL) || (MODEST_IS_RECPT_EDITOR (editor))); + + /* we check for low-mem; in that case, show a warning, and don't allow + * for the addressbook + */ + if (modest_platform_check_memory_low (MODEST_WINDOW(window), TRUE)) + return; + + priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window); + + if (editor == NULL) { + GtkWidget *view_focus; + view_focus = gtk_window_get_focus (GTK_WINDOW (window)); + + /* This code should be kept in sync with ModestRecptEditor. The + textview inside the recpt editor is the one that really gets the + focus. As it's inside a scrolled window, and this one inside the + hbox recpt editor inherits from, we'll need to go up in the + hierarchy to know if the text view is part of the recpt editor + or if it's a different text entry */ + + if (gtk_widget_get_parent (view_focus)) { + GtkWidget *first_parent; + + first_parent = gtk_widget_get_parent (view_focus); + if (gtk_widget_get_parent (first_parent) && + MODEST_IS_RECPT_EDITOR (gtk_widget_get_parent (first_parent))) { + editor = MODEST_RECPT_EDITOR (gtk_widget_get_parent (first_parent)); + } + } + + if (editor == NULL) + editor = MODEST_RECPT_EDITOR (priv->to_field); + + } + + modest_address_book_select_addresses (editor); + +} + +void +modest_msg_edit_window_select_contacts (ModestMsgEditWindow *window) +{ + g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window)); + + modest_msg_edit_window_open_addressbook (window, NULL); +} + +static void +modest_msg_edit_window_show_toolbar (ModestWindow *self, + gboolean show_toolbar) +{ + ModestWindowPrivate *parent_priv; + const gchar *action_name; + GtkAction *action; + + g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (self)); + parent_priv = MODEST_WINDOW_GET_PRIVATE(self); + + /* We can not just use the code of + modest_msg_edit_window_setup_toolbar because it has a + mixture of both initialization and creation code. */ + if (show_toolbar) + gtk_widget_show (GTK_WIDGET (parent_priv->toolbar)); + else + gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar)); + + /* Update also the actions (to update the toggles in the + menus), we have to do it manually because some other window + of the same time could have changed it (remember that the + toolbar fullscreen mode is shared by all the windows of the + same type */ + if (modest_window_mgr_get_fullscreen_mode (modest_runtime_get_window_mgr ())) + action_name = "/MenuBar/ViewMenu/ShowToolbarMenu/ViewShowToolbarFullScreenMenu"; + else + action_name = "/MenuBar/ViewMenu/ShowToolbarMenu/ViewShowToolbarNormalScreenMenu"; + + action = gtk_ui_manager_get_action (parent_priv->ui_manager, action_name); + modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), + show_toolbar); + +} + +void +modest_msg_edit_window_set_priority_flags (ModestMsgEditWindow *window, + TnyHeaderFlags priority_flags) +{ + ModestMsgEditWindowPrivate *priv; + ModestWindowPrivate *parent_priv; + + g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window)); + + priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window); + parent_priv = MODEST_WINDOW_GET_PRIVATE (window); + + if (priv->priority_flags != priority_flags) { + GtkAction *priority_action = NULL; + + priv->priority_flags = priority_flags; + + switch (priority_flags) { + case TNY_HEADER_FLAG_HIGH_PRIORITY: + gtk_image_set_from_icon_name (GTK_IMAGE (priv->priority_icon), "qgn_list_messaging_high", GTK_ICON_SIZE_MENU); + gtk_widget_show (priv->priority_icon); + priority_action = gtk_ui_manager_get_action (parent_priv->ui_manager, + "/MenuBar/ToolsMenu/MessagePriorityMenu/MessagePriorityHighMenu"); + break; + case TNY_HEADER_FLAG_LOW_PRIORITY: + gtk_image_set_from_icon_name (GTK_IMAGE (priv->priority_icon), "qgn_list_messaging_low", GTK_ICON_SIZE_MENU); + gtk_widget_show (priv->priority_icon); + priority_action = gtk_ui_manager_get_action (parent_priv->ui_manager, + "/MenuBar/ToolsMenu/MessagePriorityMenu/MessagePriorityLowMenu"); + break; + default: + gtk_widget_hide (priv->priority_icon); + priority_action = gtk_ui_manager_get_action (parent_priv->ui_manager, + "/MenuBar/ToolsMenu/MessagePriorityMenu/MessagePriorityNormalMenu"); + break; + } + gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (priority_action), TRUE); + gtk_text_buffer_set_modified (priv->text_buffer, TRUE); + } +} + +void +modest_msg_edit_window_set_file_format (ModestMsgEditWindow *window, + gint file_format) +{ + ModestMsgEditWindowPrivate *priv; + ModestWindowPrivate *parent_priv; + gint current_format; + + g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window)); + + parent_priv = MODEST_WINDOW_GET_PRIVATE (window); + priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window); + + current_format = wp_text_buffer_is_rich_text (WP_TEXT_BUFFER (priv->text_buffer)) + ? MODEST_FILE_FORMAT_FORMATTED_TEXT : MODEST_FILE_FORMAT_PLAIN_TEXT; + + if (current_format != file_format) { + switch (file_format) { + case MODEST_FILE_FORMAT_FORMATTED_TEXT: + wp_text_buffer_enable_rich_text (WP_TEXT_BUFFER (priv->text_buffer), TRUE); + remove_tags (WP_TEXT_BUFFER (priv->text_buffer)); + break; + case MODEST_FILE_FORMAT_PLAIN_TEXT: + { + GtkWidget *dialog; + gint response; + dialog = hildon_note_new_confirmation (NULL, _("emev_nc_formatting_lost")); + response = gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + if (response == GTK_RESPONSE_OK) { + wp_text_buffer_enable_rich_text (WP_TEXT_BUFFER (priv->text_buffer), FALSE); + } else { + GtkToggleAction *action = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/FileFormatMenu/FileFormatFormattedTextMenu")); + modest_utils_toggle_action_set_active_block_notify (action, TRUE); + } + } + break; + } + modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window)); + modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window)); + text_buffer_refresh_attributes (WP_TEXT_BUFFER (priv->text_buffer), window); + } +} + +void +modest_msg_edit_window_select_font (ModestMsgEditWindow *window) +{ + GtkWidget *dialog; + ModestMsgEditWindowPrivate *priv; + WPTextBufferFormat oldfmt, fmt; + gint old_position = 0; + gint response = 0; + gint position = 0; + gint font_size; + GdkColor *color = NULL; + gboolean bold, bold_set, italic, italic_set; + gboolean underline, underline_set; + gboolean strikethrough, strikethrough_set; + gboolean position_set; + gboolean font_size_set, font_set, color_set; + gchar *font_name; + + g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window)); + priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window); + + dialog = hildon_font_selection_dialog_new (GTK_WINDOW (window), NULL); + modest_window_mgr_set_modal (modest_runtime_get_window_mgr(), + GTK_WINDOW(dialog)); + + /* First we get the currently selected font information */ + wp_text_buffer_get_attributes (WP_TEXT_BUFFER (priv->text_buffer), &oldfmt, TRUE); + + switch (oldfmt.text_position) { + case TEXT_POSITION_NORMAL: + old_position = 0; + break; + case TEXT_POSITION_SUPERSCRIPT: + old_position = 1; + break; + default: + old_position = -1; + break; + } + + g_object_set (G_OBJECT (dialog), + "bold", oldfmt.bold != FALSE, + "bold-set", !oldfmt.cs.bold, + "underline", oldfmt.underline != FALSE, + "underline-set", !oldfmt.cs.underline, + "italic", oldfmt.italic != FALSE, + "italic-set", !oldfmt.cs.italic, + "strikethrough", oldfmt.strikethrough != FALSE, + "strikethrough-set", !oldfmt.cs.strikethrough, + "color", &oldfmt.color, + "color-set", !oldfmt.cs.color, + "size", wp_font_size[oldfmt.font_size], + "size-set", !oldfmt.cs.font_size, + "position", old_position, + "position-set", !oldfmt.cs.text_position, + "family", wp_get_font_name (oldfmt.font), + "family-set", !oldfmt.cs.font, + NULL); + + modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), + GTK_WINDOW (dialog)); + gtk_widget_show_all (dialog); + priv->font_dialog = dialog; + response = gtk_dialog_run (GTK_DIALOG (dialog)); + priv->font_dialog = NULL; + if (response == GTK_RESPONSE_OK) { + + g_object_get( dialog, + "bold", &bold, + "bold-set", &bold_set, + "underline", &underline, + "underline-set", &underline_set, + "italic", &italic, + "italic-set", &italic_set, + "strikethrough", &strikethrough, + "strikethrough-set", &strikethrough_set, + "color", &color, + "color-set", &color_set, + "size", &font_size, + "size-set", &font_size_set, + "family", &font_name, + "family-set", &font_set, + "position", &position, + "position-set", &position_set, + NULL ); + + } + + if (response == GTK_RESPONSE_OK) { + memset(&fmt, 0, sizeof(fmt)); + if (bold_set) { + fmt.bold = bold; + fmt.cs.bold = TRUE; + } + if (italic_set) { + fmt.italic = italic; + fmt.cs.italic = TRUE; + } + if (underline_set) { + fmt.underline = underline; + fmt.cs.underline = TRUE; + } + if (strikethrough_set) { + fmt.strikethrough = strikethrough; + fmt.cs.strikethrough = TRUE; + } + if (position_set) { + fmt.text_position = + ( position == 0 ) + ? TEXT_POSITION_NORMAL + : ( ( position == 1 ) + ? TEXT_POSITION_SUPERSCRIPT + : TEXT_POSITION_SUBSCRIPT ); + fmt.cs.text_position = TRUE; + fmt.font_size = oldfmt.font_size; + } + if (color_set) { + fmt.color = *color; + fmt.cs.color = TRUE; + } + if (font_set) { + fmt.font = wp_get_font_index(font_name, + DEFAULT_FONT); + fmt.cs.font = TRUE; + } + g_free(font_name); + if (font_size_set) { + fmt.cs.font_size = TRUE; + fmt.font_size = wp_get_font_size_index(font_size, DEFAULT_FONT_SIZE); + } + wp_text_buffer_set_format(WP_TEXT_BUFFER(priv->text_buffer), &fmt); + text_buffer_refresh_attributes (WP_TEXT_BUFFER (priv->text_buffer), window); + } + gtk_widget_destroy (dialog); + + gtk_widget_grab_focus(GTK_WIDGET(priv->msg_body)); +} + +void +modest_msg_edit_window_undo (ModestMsgEditWindow *window) +{ + ModestMsgEditWindowPrivate *priv; + + g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window)); + priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window); + + wp_text_buffer_undo (WP_TEXT_BUFFER (priv->text_buffer)); + + modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window)); + modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window)); +} + +void +modest_msg_edit_window_redo (ModestMsgEditWindow *window) +{ + ModestMsgEditWindowPrivate *priv; + + g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window)); + priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window); + + wp_text_buffer_redo (WP_TEXT_BUFFER (priv->text_buffer)); + + modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window)); + modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window)); + +} + +static void +text_buffer_can_undo (GtkTextBuffer *buffer, gboolean can_undo, ModestMsgEditWindow *window) +{ + ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window); + + priv->can_undo = can_undo; +} + +static void +text_buffer_can_redo (GtkTextBuffer *buffer, gboolean can_redo, ModestMsgEditWindow *window) +{ + ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window); + + priv->can_redo = can_redo; +} + +gboolean +modest_msg_edit_window_can_undo (ModestMsgEditWindow *window) +{ + ModestMsgEditWindowPrivate *priv; + g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window), FALSE); + priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window); + + return priv->can_undo; +} + +gboolean +modest_msg_edit_window_can_redo (ModestMsgEditWindow *window) +{ + ModestMsgEditWindowPrivate *priv; + g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window), FALSE); + priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window); + + return priv->can_redo; +} + + +static void +text_buffer_delete_images_by_id (GtkTextBuffer *buffer, const gchar * image_id) +{ + GtkTextIter iter; + GtkTextIter match_start, match_end; + + if (image_id == NULL) + return; + + gtk_text_buffer_get_start_iter (buffer, &iter); + + while (gtk_text_iter_forward_search (&iter, "\xef\xbf\xbc", 0, &match_start, &match_end, NULL)) { + GSList *tags = gtk_text_iter_get_tags (&match_start); + GSList *node; + for (node = tags; node != NULL; node = g_slist_next (node)) { + GtkTextTag *tag = (GtkTextTag *) node->data; + if (g_object_get_data (G_OBJECT (tag), "image-set") != NULL) { + gchar *cur_image_id = g_object_get_data (G_OBJECT (tag), "image-index"); + if ((cur_image_id != NULL) && (strcmp (image_id, cur_image_id)==0)) { + gint offset; + offset = gtk_text_iter_get_offset (&match_start); + gtk_text_buffer_delete (buffer, &match_start, &match_end); + gtk_text_buffer_get_iter_at_offset (buffer, &iter, offset); + } + } + } + gtk_text_iter_forward_char (&iter); + } +} + +gboolean +message_is_empty (ModestMsgEditWindow *window) +{ + ModestMsgEditWindowPrivate *priv = NULL; + + g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window), FALSE); + priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window); + + /** TODO: Add wpeditor API to tell us if there is any _visible_ text, + * so we can ignore markup. + */ + GtkTextBuffer *buf = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->msg_body)); + gint count = 0; + if (buf) + count = gtk_text_buffer_get_char_count (buf); + + return count == 0; +} + +static gboolean +msg_body_focus (GtkWidget *focus, + GdkEventFocus *event, + gpointer userdata) +{ + + modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (userdata)); + modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (userdata)); + modest_window_check_dimming_rules_group (MODEST_WINDOW (userdata), MODEST_DIMMING_RULES_CLIPBOARD); + return FALSE; +} + +static void +recpt_field_changed (GtkTextBuffer *buffer, + ModestMsgEditWindow *editor) +{ + modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (editor)); + modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (editor)); +} + +static void +body_changed (GtkTextBuffer *buffer, ModestMsgEditWindow *editor) +{ + modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (editor)); + modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (editor)); +} + +void +modest_msg_edit_window_set_modified (ModestMsgEditWindow *editor, + gboolean modified) +{ + ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (editor); + GtkTextBuffer *buffer; + + buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->to_field)); + gtk_text_buffer_set_modified (buffer, modified); + buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->cc_field)); + gtk_text_buffer_set_modified (buffer, modified); + buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->bcc_field)); + gtk_text_buffer_set_modified (buffer, modified); + gtk_text_buffer_set_modified (priv->text_buffer, modified); +} + +gboolean +modest_msg_edit_window_is_modified (ModestMsgEditWindow *editor) +{ + ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (editor); + const char *account_name; + GtkTextBuffer *buffer; + + buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->to_field)); + if (gtk_text_buffer_get_modified (buffer)) + return TRUE; + buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->cc_field)); + if (gtk_text_buffer_get_modified (buffer)) + return TRUE; + buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->bcc_field)); + if (gtk_text_buffer_get_modified (buffer)) + return TRUE; + if (gtk_text_buffer_get_modified (priv->text_buffer)) + return TRUE; + account_name = modest_combo_box_get_active_id (MODEST_COMBO_BOX (priv->from_field)); + if (!priv->original_account_name || strcmp(account_name, priv->original_account_name)) { + return TRUE; + } + + return FALSE; +} + + + + +gboolean +modest_msg_edit_window_check_names (ModestMsgEditWindow *window, gboolean add_to_addressbook) +{ + ModestMsgEditWindowPrivate *priv = NULL; + + g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window), FALSE); + priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window); + + /* check if there's no recipient added */ + if ((gtk_text_buffer_get_char_count (modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR (priv->to_field))) == 0) && + (gtk_text_buffer_get_char_count (modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR (priv->cc_field))) == 0) && + (gtk_text_buffer_get_char_count (modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR (priv->bcc_field))) == 0)) { + /* no recipient contents, then select contacts */ + modest_msg_edit_window_open_addressbook (window, NULL); + return FALSE; + } + + if (!modest_address_book_check_names (MODEST_RECPT_EDITOR (priv->to_field), add_to_addressbook)) { + modest_recpt_editor_grab_focus (MODEST_RECPT_EDITOR (priv->to_field)); + return FALSE; + } + if (!modest_address_book_check_names (MODEST_RECPT_EDITOR (priv->cc_field), add_to_addressbook)) { + modest_recpt_editor_grab_focus (MODEST_RECPT_EDITOR (priv->cc_field)); + return FALSE; + } + if (!modest_address_book_check_names (MODEST_RECPT_EDITOR (priv->bcc_field), add_to_addressbook)) { + modest_recpt_editor_grab_focus (MODEST_RECPT_EDITOR (priv->bcc_field)); + return FALSE; + } + + if (!modest_recpt_editor_has_focus (MODEST_RECPT_EDITOR (priv->cc_field)) && + !modest_recpt_editor_has_focus (MODEST_RECPT_EDITOR (priv->bcc_field))) + modest_recpt_editor_grab_focus (MODEST_RECPT_EDITOR (priv->to_field)); + + return TRUE; + +} + +static void +modest_msg_edit_window_add_attachment_clicked (GtkButton *button, + ModestMsgEditWindow *window) +{ + modest_msg_edit_window_offer_attach_file (window); +} + +const gchar * +modest_msg_edit_window_get_clipboard_text (ModestMsgEditWindow *win) +{ + ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (win); + + return priv->clipboard_text; +} + +static void +modest_msg_edit_window_clipboard_owner_change (GtkClipboard *clipboard, + GdkEvent *event, + ModestMsgEditWindow *window) +{ + ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window); + GtkClipboard *selection_clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD); + gchar *text = NULL; + if (!GTK_WIDGET_VISIBLE (window)) + return; + + g_object_ref (window); + text = gtk_clipboard_wait_for_text (selection_clipboard); + + if (priv->clipboard_text != NULL) { + g_free (priv->clipboard_text); + } + priv->clipboard_text = text; + + if (GTK_WIDGET_VISIBLE (window)) { + modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD); + } + g_object_unref (window); +} + +static gboolean clipboard_owner_change_idle (gpointer userdata) +{ + ModestMsgEditWindow *window = (ModestMsgEditWindow *) userdata; + ModestMsgEditWindowPrivate *priv; + + gdk_threads_enter (); + g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window), FALSE); + priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window); + + priv->clipboard_owner_idle = 0; + modest_msg_edit_window_clipboard_owner_change (NULL, NULL, window); + gdk_threads_leave (); + + return FALSE; +} + +static void +modest_msg_edit_window_clipboard_owner_handle_change_in_idle (ModestMsgEditWindow *window) +{ + ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window); + if (priv->clipboard_owner_idle == 0) { + priv->clipboard_owner_idle = g_idle_add (clipboard_owner_change_idle, window); + } +} + +static void +subject_field_move_cursor (GtkEntry *entry, + GtkMovementStep step, + gint a1, + gboolean a2, + gpointer window) +{ + if (!GTK_WIDGET_VISIBLE (window)) + return; + + modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD); +} + +static void +update_window_title (ModestMsgEditWindow *window) +{ + ModestMsgEditWindowPrivate *priv = NULL; + const gchar *subject; + + priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window); + subject = gtk_entry_get_text (GTK_ENTRY (priv->subject_field)); + if (subject == NULL || subject[0] == '\0') + subject = _("mail_va_new_email"); + + gtk_window_set_title (GTK_WINDOW (window), subject); + +} + +static void +subject_field_changed (GtkEditable *editable, + ModestMsgEditWindow *window) +{ + ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window); + update_window_title (window); + gtk_text_buffer_set_modified (priv->text_buffer, TRUE); + modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window)); + modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window)); +} + +static void +subject_field_insert_text (GtkEditable *editable, + gchar *new_text, + gint new_text_length, + gint *position, + ModestMsgEditWindow *window) +{ + GString *result = g_string_new (""); + gchar *current; + gint result_len = 0; + const gchar *entry_text = NULL; + gint old_length; + + entry_text = gtk_entry_get_text (GTK_ENTRY (editable)); + old_length = g_utf8_strlen (entry_text, -1); + + for (current = new_text; current != NULL && *current != '\0'; current = g_utf8_next_char (current)) { + gunichar c = g_utf8_get_char_validated (current, 8); + /* Invalid unichar, stop */ + if (c == -1) + break; + /* a bullet */ + if (c == 0x2022) + continue; + result = g_string_append_unichar (result, c); + result_len++; + } + + if (MIN (result_len, 1000) != g_utf8_strlen (new_text, 1000)) { + g_signal_stop_emission_by_name (G_OBJECT (editable), "insert-text"); + if (result_len > 0) + { + /* Prevent endless recursion */ + g_signal_handlers_block_by_func(G_OBJECT(editable), G_CALLBACK(subject_field_insert_text), window); + g_signal_emit_by_name (editable, "insert-text", + (gpointer) result->str, (gpointer) result->len, + (gpointer) position, (gpointer) window); + g_signal_handlers_unblock_by_func(G_OBJECT(editable), G_CALLBACK(subject_field_insert_text), window); + } + } + + if (result_len + old_length > 1000) { + hildon_banner_show_information (GTK_WIDGET (window), NULL, + dgettext("hildon-common-strings", + "ckdg_ib_maximum_characters_reached")); + } + + g_string_free (result, TRUE); +} + +void +modest_msg_edit_window_toggle_find_toolbar (ModestMsgEditWindow *window, + gboolean show) +{ + ModestMsgEditWindowPrivate *priv = NULL; + + g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window)); + priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window); + + gtk_widget_set_no_show_all (priv->find_toolbar, FALSE); + + if (show) { + gtk_widget_show_all (priv->find_toolbar); + hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE); + } else { + gtk_widget_hide_all (priv->find_toolbar); + gtk_widget_grab_focus (priv->msg_body); + } + +} + +static gboolean +gtk_text_iter_forward_search_insensitive (const GtkTextIter *iter, + const gchar *str, + GtkTextIter *match_start, + GtkTextIter *match_end) +{ + GtkTextIter end_iter; + gchar *str_casefold; + gint str_chars_n; + gchar *range_text; + gchar *range_casefold; + gint offset; + gint range_chars_n; + gboolean result = FALSE; + + if (str == NULL) + return TRUE; + + /* get end iter */ + end_iter = *iter; + gtk_text_iter_forward_to_end (&end_iter); + + str_casefold = g_utf8_casefold (str, -1); + str_chars_n = strlen (str); + + range_text = gtk_text_iter_get_visible_text (iter, &end_iter); + range_casefold = g_utf8_casefold (range_text, -1); + range_chars_n = strlen (range_casefold); + + if (range_chars_n < str_chars_n) { + g_free (str_casefold); + g_free (range_text); + g_free (range_casefold); + return FALSE; + } + + for (offset = 0; offset <= range_chars_n - str_chars_n; offset++) { + gchar *range_subtext = g_strndup (range_casefold + offset, str_chars_n); + if (!g_utf8_collate (range_subtext, str_casefold)) { + gchar *found_text = g_strndup (range_text + offset, str_chars_n); + result = TRUE; + gtk_text_iter_forward_search (iter, found_text, GTK_TEXT_SEARCH_VISIBLE_ONLY|GTK_TEXT_SEARCH_TEXT_ONLY, + match_start, match_end, NULL); + g_free (found_text); + } + g_free (range_subtext); + if (result) + break; + } + g_free (str_casefold); + g_free (range_text); + g_free (range_casefold); + + return result; +} + + +static void +modest_msg_edit_window_find_toolbar_search (GtkWidget *widget, + ModestMsgEditWindow *window) +{ + gchar *current_search = NULL; + ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window); + gboolean result; + GtkTextIter selection_start, selection_end; + GtkTextIter match_start, match_end; + gboolean continue_search = FALSE; + + if (message_is_empty (window)) { + g_free (priv->last_search); + priv->last_search = NULL; + hildon_banner_show_information (GTK_WIDGET (window), NULL, _("mail_ib_nothing_to_find")); + return; + } + + g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL); + if ((current_search == NULL) || (strcmp (current_search, "") == 0)) { + g_free (current_search); + g_free (priv->last_search); + priv->last_search = NULL; + /* Information banner about empty search */ + hildon_banner_show_information (NULL, NULL, dgettext ("hildon-common-strings", "ecdg_ib_find_rep_enter_text")); + return; + } + + if ((priv->last_search != NULL)&&(!strcmp (current_search, priv->last_search))) { + continue_search = TRUE; + } else { + g_free (priv->last_search); + priv->last_search = g_strdup (current_search); + } + + if (continue_search) { + gtk_text_buffer_get_selection_bounds (priv->text_buffer, &selection_start, &selection_end); + result = gtk_text_iter_forward_search_insensitive (&selection_end, current_search, + &match_start, &match_end); + if (!result) + hildon_banner_show_information (NULL, NULL, dgettext ("hildon-libs", "ckct_ib_find_search_complete")); + } else { + GtkTextIter buffer_start; + gtk_text_buffer_get_start_iter (priv->text_buffer, &buffer_start); + result = gtk_text_iter_forward_search_insensitive (&buffer_start, current_search, + &match_start, &match_end); + if (!result) + hildon_banner_show_information (NULL, NULL, dgettext ("hildon-libs", "ckct_ib_find_no_matches")); + } + + /* Mark as selected the string found in search */ + if (result) { + gtk_text_buffer_select_range (priv->text_buffer, &match_start, &match_end); + gtk_text_view_scroll_to_iter (GTK_TEXT_VIEW (priv->msg_body), &match_start, 0.0, TRUE, 0.0, 0.0); + correct_scroll_without_drag_check (MODEST_MSG_EDIT_WINDOW (window), FALSE); + } else { + g_free (priv->last_search); + priv->last_search = NULL; + } + g_free (current_search); +} + +static void +modest_msg_edit_window_find_toolbar_close (GtkWidget *widget, + ModestMsgEditWindow *window) +{ + GtkToggleAction *toggle; + ModestWindowPrivate *parent_priv; + parent_priv = MODEST_WINDOW_GET_PRIVATE (window); + + toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ToolsMenu/FindInMessageMenu")); + gtk_toggle_action_set_active (toggle, FALSE); +} + +gboolean +modest_msg_edit_window_get_sent (ModestMsgEditWindow *window) +{ + ModestMsgEditWindowPrivate *priv; + + priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE(window); + return priv->sent; +} + +void +modest_msg_edit_window_set_sent (ModestMsgEditWindow *window, + gboolean sent) +{ + ModestMsgEditWindowPrivate *priv; + + priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE(window); + priv->sent = sent; +} + + +void +modest_msg_edit_window_set_draft (ModestMsgEditWindow *window, + TnyMsg *draft) +{ + ModestMsgEditWindowPrivate *priv; + TnyHeader *header = NULL; + + g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window)); + g_return_if_fail ((draft == NULL)||(TNY_IS_MSG (draft))); + + priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window); + ModestWindowMgr *mgr = modest_runtime_get_window_mgr (); + + if (priv->draft_msg != NULL) { + g_object_unref (priv->draft_msg); + } + + if (draft != NULL) { + g_object_ref (draft); + header = tny_msg_get_header (draft); + if (priv->msg_uid) { + g_free (priv->msg_uid); + priv->msg_uid = NULL; + } + priv->msg_uid = modest_tny_folder_get_header_unique_id (header); + if (GTK_WIDGET_REALIZED (window)) + modest_window_mgr_register_window (mgr, MODEST_WINDOW (window)); + } + + priv->draft_msg = draft; +} + +static void +text_buffer_apply_tag (GtkTextBuffer *buffer, GtkTextTag *tag, + GtkTextIter *start, GtkTextIter *end, + gpointer userdata) +{ + ModestMsgEditWindow *window = MODEST_MSG_EDIT_WINDOW (userdata); + ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (userdata); + gchar *tag_name; + + if (tag == NULL+13) return; + g_object_get (G_OBJECT (tag), "name", &tag_name, NULL); + if ((tag_name != NULL) && (g_str_has_prefix (tag_name, "image-tag-replace-"))) { + replace_with_images (window, priv->images); + } +} + +void +modest_msg_edit_window_add_part (ModestMsgEditWindow *window, + TnyMimePart *part) +{ + ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window); + + g_return_if_fail (TNY_IS_MIME_PART (part)); + tny_list_prepend (priv->attachments, (GObject *) part); + modest_attachments_view_add_attachment (MODEST_ATTACHMENTS_VIEW (priv->attachments_view), part, TRUE, 0); + gtk_widget_set_no_show_all (priv->attachments_caption, FALSE); + gtk_widget_show_all (priv->attachments_caption); + gtk_text_buffer_set_modified (priv->text_buffer, TRUE); +} + +const gchar* +modest_msg_edit_window_get_message_uid (ModestMsgEditWindow *window) +{ + ModestMsgEditWindowPrivate *priv; + + g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window), NULL); + priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window); + + return priv->msg_uid; +} + +GtkWidget * +modest_msg_edit_window_get_child_widget (ModestMsgEditWindow *win, + ModestMsgEditWindowWidgetType widget_type) +{ + ModestMsgEditWindowPrivate *priv; + + g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (win), NULL); + priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (win); + + switch (widget_type) { + case MODEST_MSG_EDIT_WINDOW_WIDGET_TYPE_BODY: + return priv->msg_body; + break; + case MODEST_MSG_EDIT_WINDOW_WIDGET_TYPE_TO: + return priv->to_field; + break; + case MODEST_MSG_EDIT_WINDOW_WIDGET_TYPE_CC: + return priv->cc_field; + break; + case MODEST_MSG_EDIT_WINDOW_WIDGET_TYPE_BCC: + return priv->bcc_field; + break; + case MODEST_MSG_EDIT_WINDOW_WIDGET_TYPE_SUBJECT: + return priv->subject_field; + break; + case MODEST_MSG_EDIT_WINDOW_WIDGET_TYPE_ATTACHMENTS: + return priv->attachments_view; + break; + default: + return NULL; + } +} + +static void +remove_tags (WPTextBuffer *buffer) +{ + GtkTextIter start, end; + + gtk_text_buffer_get_start_iter (GTK_TEXT_BUFFER (buffer), &start); + gtk_text_buffer_get_end_iter (GTK_TEXT_BUFFER (buffer), &end); + + gtk_text_buffer_remove_all_tags (GTK_TEXT_BUFFER (buffer), &start, &end); +} + +static void +on_account_removed (TnyAccountStore *account_store, + TnyAccount *account, + gpointer user_data) +{ + /* Do nothing if it's a store account, because we use the + transport to send the messages */ + if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_TRANSPORT) { + const gchar *parent_acc = NULL; + const gchar *our_acc = NULL; + + our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data)); + parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account); + /* Close this window if I'm showing a message of the removed account */ + if (strcmp (parent_acc, our_acc) == 0) + modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data)); + } +} + +static gboolean +on_zoom_minus_plus_not_implemented (ModestWindow *window) +{ + g_return_val_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window), FALSE); + + hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_cannot_zoom_here")); + return FALSE; + +} + +static void +set_zoom_do_nothing (ModestWindow *window, + gdouble zoom) +{ +} + +static gdouble +get_zoom_do_nothing (ModestWindow *window) +{ + return 1.0; +} + diff --git a/src/hildon2/modest-msg-view-window-ui-dimming.h b/src/hildon2/modest-msg-view-window-ui-dimming.h new file mode 100644 index 0000000..79f678c --- /dev/null +++ b/src/hildon2/modest-msg-view-window-ui-dimming.h @@ -0,0 +1,76 @@ +#ifndef __MODEST_MSG_VIEW_WINDOW_UI_DIMMING_PRIV_H__ +#define __MODEST_MSG_VIEW_WINDOW_UI_DIMMING_PRIV_H__ + +#include "modest-dimming-rules-group.h" +#include "modest-ui-dimming-rules.h" + +G_BEGIN_DECLS + + +/* Menu Dimming rules entries */ +static const ModestDimmingEntry modest_msg_view_menu_dimming_entries [] = { + + /* Message Menu */ + { "/MenuBar/MessageMenu/MessageNewMenu", G_CALLBACK(modest_ui_dimming_rules_on_new_msg) }, + { "/MenuBar/MessageMenu/MessageReplyMenu", G_CALLBACK(modest_ui_dimming_rules_on_reply_msg) }, + { "/MenuBar/MessageMenu/MessageReplyAllMenu", G_CALLBACK(modest_ui_dimming_rules_on_reply_msg) }, + { "/MenuBar/MessageMenu/MessageForwardMenu", G_CALLBACK(modest_ui_dimming_rules_on_reply_msg) }, + { "/MenuBar/MessageMenu/MessageDeleteMenu", G_CALLBACK(modest_ui_dimming_rules_on_delete_msg) }, + { "/MenuBar/MessageMenu/MessageDetailsMenu", G_CALLBACK(modest_ui_dimming_rules_on_details) }, + + /* Edit Menu */ + { "/MenuBar/EditMenu", NULL }, + { "/MenuBar/EditMenu/EditPasteMenu", G_CALLBACK(modest_ui_dimming_rules_always_dimmed) }, + { "/MenuBar/EditMenu/EditSelectAllMenu", NULL }, + { "/MenuBar/EditMenu/EditMoveToMenu", G_CALLBACK(modest_ui_dimming_rules_on_move_to) }, + + /* View Menu */ + { "/MenuBar/ViewMenu", NULL }, + { "/MenuBar/ViewMenu/ZoomMenu", NULL }, + { "/MenuBar/ViewMenu/ViewToggleFullscreenMenu", NULL }, + { "/MenuBar/ViewMenu/ViewPreviousMessageMenu", G_CALLBACK(modest_ui_dimming_rules_on_view_previous) }, + { "/MenuBar/ViewMenu/ViewNextMessageMenu", G_CALLBACK(modest_ui_dimming_rules_on_view_next)}, + + /* Attachments Menu */ + { "/MenuBar/AttachmentsMenu", NULL }, + { "/MenuBar/AttachmentsMenu/ViewAttachmentMenu", G_CALLBACK(modest_ui_dimming_rules_on_view_attachments) }, + { "/MenuBar/AttachmentsMenu/SaveAttachmentMenu", G_CALLBACK(modest_ui_dimming_rules_on_save_attachments) }, + { "/MenuBar/AttachmentsMenu/RemoveAttachmentMenu", G_CALLBACK(modest_ui_dimming_rules_on_remove_attachments) }, + + /* Tools Menu */ + { "/MenuBar/ToolsMenu", NULL }, + + /* Close Menu */ + { "/MenuBar/CloseMenu", NULL }, + { "/MenuBar/ToolsMenu/CloseWindowMenu", NULL }, + { "/MenuBar/ToolsMenu/CloseAllWindowsMenu", NULL }, + + /* Contextual Menus (Toolbar) */ + { "/ToolbarReplyCSM/MessageForwardMenu", NULL }, + { "/ToolbarReplyCSM/MessageReplyAllMenu", NULL }, + { "/ToolbarReplyCSM/MessageReplyMenu", NULL }, + +}; + +/* Clipboard status dimming rule entries */ +static const ModestDimmingEntry modest_msg_view_clipboard_dimming_entries [] = { + { "/MenuBar/EditMenu/EditCutMenu", G_CALLBACK(modest_ui_dimming_rules_always_dimmed) }, + { "/MenuBar/EditMenu/EditCopyMenu", G_CALLBACK(modest_ui_dimming_rules_on_copy) }, + { "/MenuBar/ToolsMenu/ToolsAddToContactsMenu", G_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts) }, +}; + +/* Menu Dimming rules entries */ +static const ModestDimmingEntry modest_msg_view_toolbar_dimming_entries [] = { + + /* Toolbar */ + { "/ToolBar/ToolbarMessageReply", G_CALLBACK(modest_ui_dimming_rules_on_reply_msg) }, + { "/ToolBar/ToolbarMessageMoveTo", G_CALLBACK(modest_ui_dimming_rules_on_move_to) }, + { "/ToolBar/ToolbarDeleteMessage", G_CALLBACK(modest_ui_dimming_rules_on_delete_msg) }, + { "/ToolBar/FindInMessage", G_CALLBACK(modest_ui_dimming_rules_on_find_msg) }, + { "/ToolBar/ToolbarMessageBack", G_CALLBACK(modest_ui_dimming_rules_on_view_previous) }, + { "/ToolBar/ToolbarMessageNext", G_CALLBACK(modest_ui_dimming_rules_on_view_next) }, + { "/ToolBar/ToolbarCancel", NULL }, +}; + +G_END_DECLS +#endif /* __MODEST_MSG_VIEW_WINDOW_UI_PRIV_H__ */ diff --git a/src/hildon2/modest-msg-view-window.c b/src/hildon2/modest-msg-view-window.c new file mode 100644 index 0000000..4f1f139 --- /dev/null +++ b/src/hildon2/modest-msg-view-window.c @@ -0,0 +1,3200 @@ +/* Copyright (c) 2006, Nokia Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Nokia Corporation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include +#include +#include +#include +#include +#include +#include +#include "modest-marshal.h" +#include "modest-platform.h" +#include +#include +#include +#include +#include +#include "modest-msg-view-window-ui-dimming.h" +#include +#include +#include +#include +#include +#include +#include "modest-progress-bar.h" +#include "modest-defs.h" +#include "modest-hildon-includes.h" +#include "modest-ui-dimming-manager.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEFAULT_FOLDER "MyDocs/.documents" + +static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass); +static void modest_msg_view_window_init (ModestMsgViewWindow *obj); +static void modest_header_view_observer_init( + ModestHeaderViewObserverIface *iface_class); +static void modest_msg_view_window_finalize (GObject *obj); +static void modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *obj, + gpointer data); +static void modest_msg_view_window_find_toolbar_close (GtkWidget *widget, + ModestMsgViewWindow *obj); +static void modest_msg_view_window_find_toolbar_search (GtkWidget *widget, + ModestMsgViewWindow *obj); + +static void modest_msg_view_window_disconnect_signals (ModestWindow *self); +static void modest_msg_view_window_set_zoom (ModestWindow *window, + gdouble zoom); +static gdouble modest_msg_view_window_get_zoom (ModestWindow *window); +static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window); +static gboolean modest_msg_view_window_zoom_plus (ModestWindow *window); +static gboolean modest_msg_view_window_key_event (GtkWidget *window, + GdkEventKey *event, + gpointer userdata); +static gboolean modest_msg_view_window_window_state_event (GtkWidget *widget, + GdkEventWindowState *event, + gpointer userdata); +static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window); + +static void modest_msg_view_window_show_toolbar (ModestWindow *window, + gboolean show_toolbar); + +static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard, + GdkEvent *event, + ModestMsgViewWindow *window); + +static void modest_msg_view_window_on_row_changed (GtkTreeModel *header_model, + GtkTreePath *arg1, + GtkTreeIter *arg2, + ModestMsgViewWindow *window); + +static void modest_msg_view_window_on_row_deleted (GtkTreeModel *header_model, + GtkTreePath *arg1, + ModestMsgViewWindow *window); + +static void modest_msg_view_window_on_row_inserted (GtkTreeModel *header_model, + GtkTreePath *tree_path, + GtkTreeIter *tree_iter, + ModestMsgViewWindow *window); + +static void modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model, + GtkTreePath *arg1, + GtkTreeIter *arg2, + gpointer arg3, + ModestMsgViewWindow *window); + +static void modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *window, + GtkTreeModel *model, + const gchar *tny_folder_id); + +static void cancel_progressbar (GtkToolButton *toolbutton, + ModestMsgViewWindow *self); + +static void on_queue_changed (ModestMailOperationQueue *queue, + ModestMailOperation *mail_op, + ModestMailOperationQueueNotification type, + ModestMsgViewWindow *self); + +static void on_account_removed (TnyAccountStore *account_store, + TnyAccount *account, + gpointer user_data); + +static void on_move_focus (GtkWidget *widget, + GtkDirectionType direction, + gpointer userdata); + +static void view_msg_cb (ModestMailOperation *mail_op, + TnyHeader *header, + gboolean canceled, + TnyMsg *msg, + GError *error, + gpointer user_data); + +static void set_toolbar_mode (ModestMsgViewWindow *self, + ModestToolBarModes mode); + +static void update_window_title (ModestMsgViewWindow *window); + +static gboolean set_toolbar_transfer_mode (ModestMsgViewWindow *self); +static void init_window (ModestMsgViewWindow *obj); + +static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox); + +static void check_dimming_rules_after_change (ModestMsgViewWindow *window); + +static gboolean on_fetch_image (ModestMsgView *msgview, + const gchar *uri, + TnyStream *stream, + ModestMsgViewWindow *window); + +static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self, + GtkScrollType scroll_type, + gboolean horizontal, + gpointer userdata); + +/* list my signals */ +enum { + MSG_CHANGED_SIGNAL, + SCROLL_CHILD_SIGNAL, + LAST_SIGNAL +}; + +static const GtkToggleActionEntry msg_view_toggle_action_entries [] = { + { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), NULL, NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE }, + { "ToolsFindInMessage", NULL, N_("mcen_me_viewer_find"), "F", NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE }, +}; + +static const GtkRadioActionEntry msg_view_zoom_action_entries [] = { + { "Zoom50", NULL, N_("mcen_me_viewer_50"), NULL, NULL, 50 }, + { "Zoom80", NULL, N_("mcen_me_viewer_80"), NULL, NULL, 80 }, + { "Zoom100", NULL, N_("mcen_me_viewer_100"), NULL, NULL, 100 }, + { "Zoom120", NULL, N_("mcen_me_viewer_120"), NULL, NULL, 120 }, + { "Zoom150", NULL, N_("mcen_me_viewer_150"), NULL, NULL, 150 }, + { "Zoom200", NULL, N_("mcen_me_viewer_200"), NULL, NULL, 200 } +}; + +typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate; +struct _ModestMsgViewWindowPrivate { + + GtkWidget *msg_view; + GtkWidget *main_scroll; + GtkWidget *find_toolbar; + gchar *last_search; + + /* Progress observers */ + GtkWidget *progress_bar; + GSList *progress_widgets; + + /* Tollbar items */ + GtkWidget *progress_toolitem; + GtkWidget *cancel_toolitem; + GtkWidget *prev_toolitem; + GtkWidget *next_toolitem; + ModestToolBarModes current_toolbar_mode; + + /* Optimized view enabled */ + gboolean optimized_view; + + /* Whether this was created via the *_new_for_search_result() function. */ + gboolean is_search_result; + + /* Whether the message is in outbox */ + gboolean is_outbox; + + /* A reference to the @model of the header view + * to allow selecting previous/next messages, + * if the message is currently selected in the header view. + */ + const gchar *header_folder_id; + GtkTreeModel *header_model; + GtkTreeRowReference *row_reference; + GtkTreeRowReference *next_row_reference; + + gulong clipboard_change_handler; + gulong queue_change_handler; + gulong account_removed_handler; + gulong row_changed_handler; + gulong row_deleted_handler; + gulong row_inserted_handler; + gulong rows_reordered_handler; + + guint purge_timeout; + GtkWidget *remove_attachment_banner; + + guint progress_bar_timeout; + + gchar *msg_uid; + + GSList *sighandlers; +}; + +#define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \ + MODEST_TYPE_MSG_VIEW_WINDOW, \ + ModestMsgViewWindowPrivate)) +/* globals */ +static GtkWindowClass *parent_class = NULL; + +/* uncomment the following if you have defined any signals */ +static guint signals[LAST_SIGNAL] = {0}; + +GType +modest_msg_view_window_get_type (void) +{ + static GType my_type = 0; + if (!my_type) { + static const GTypeInfo my_info = { + sizeof(ModestMsgViewWindowClass), + NULL, /* base init */ + NULL, /* base finalize */ + (GClassInitFunc) modest_msg_view_window_class_init, + NULL, /* class finalize */ + NULL, /* class data */ + sizeof(ModestMsgViewWindow), + 1, /* n_preallocs */ + (GInstanceInitFunc) modest_msg_view_window_init, + NULL + }; + my_type = g_type_register_static (MODEST_TYPE_WINDOW, + "ModestMsgViewWindow", + &my_info, 0); + + static const GInterfaceInfo modest_header_view_observer_info = + { + (GInterfaceInitFunc) modest_header_view_observer_init, + NULL, /* interface_finalize */ + NULL /* interface_data */ + }; + + g_type_add_interface_static (my_type, + MODEST_TYPE_HEADER_VIEW_OBSERVER, + &modest_header_view_observer_info); + } + return my_type; +} + +static void +save_state (ModestWindow *self) +{ + modest_widget_memory_save (modest_runtime_get_conf (), + G_OBJECT(self), + MODEST_CONF_MSG_VIEW_WINDOW_KEY); +} + + +static void +restore_settings (ModestMsgViewWindow *self) +{ + ModestConf *conf; + ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (self); + GtkAction *action; + + conf = modest_runtime_get_conf (); + action = gtk_ui_manager_get_action (parent_priv->ui_manager, + "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarNormalScreenMenu"); + gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), + modest_conf_get_bool (conf, MODEST_CONF_MSG_VIEW_WINDOW_SHOW_TOOLBAR, NULL)); + action = gtk_ui_manager_get_action (parent_priv->ui_manager, + "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarFullScreenMenu"); + gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), + modest_conf_get_bool (conf, MODEST_CONF_MSG_VIEW_WINDOW_SHOW_TOOLBAR_FULLSCREEN, NULL)); + modest_widget_memory_restore (conf, + G_OBJECT(self), + MODEST_CONF_MSG_VIEW_WINDOW_KEY); +} + +static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self, + GtkScrollType scroll_type, + gboolean horizontal, + gpointer userdata) +{ + ModestMsgViewWindowPrivate *priv; + gboolean return_value; + + priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self); + g_signal_emit_by_name (priv->main_scroll, "scroll-child", scroll_type, horizontal, &return_value); + return return_value; +} + +static void +add_scroll_binding (GtkBindingSet *binding_set, + guint keyval, + GtkScrollType scroll) +{ + guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left; + + gtk_binding_entry_add_signal (binding_set, keyval, 0, + "scroll_child", 2, + GTK_TYPE_SCROLL_TYPE, scroll, + G_TYPE_BOOLEAN, FALSE); + gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0, + "scroll_child", 2, + GTK_TYPE_SCROLL_TYPE, scroll, + G_TYPE_BOOLEAN, FALSE); +} + +static void +modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass) +{ + GObjectClass *gobject_class; + ModestWindowClass *modest_window_class; + GtkBindingSet *binding_set; + + gobject_class = (GObjectClass*) klass; + modest_window_class = (ModestWindowClass *) klass; + + parent_class = g_type_class_peek_parent (klass); + gobject_class->finalize = modest_msg_view_window_finalize; + + modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom; + modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom; + modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus; + modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus; + modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar; + modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals; + + modest_window_class->save_state_func = save_state; + + klass->scroll_child = modest_msg_view_window_scroll_child; + + signals[MSG_CHANGED_SIGNAL] = + g_signal_new ("msg-changed", + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed), + NULL, NULL, + modest_marshal_VOID__POINTER_POINTER, + G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER); + + signals[SCROLL_CHILD_SIGNAL] = + g_signal_new ("scroll-child", + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (ModestMsgViewWindowClass, scroll_child), + NULL, NULL, + modest_marshal_BOOLEAN__ENUM_BOOLEAN, + G_TYPE_BOOLEAN, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_BOOLEAN); + + binding_set = gtk_binding_set_by_class (klass); + add_scroll_binding (binding_set, GDK_Up, GTK_SCROLL_STEP_UP); + add_scroll_binding (binding_set, GDK_Down, GTK_SCROLL_STEP_DOWN); + add_scroll_binding (binding_set, GDK_Page_Up, GTK_SCROLL_PAGE_UP); + add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_DOWN); + add_scroll_binding (binding_set, GDK_Home, GTK_SCROLL_START); + add_scroll_binding (binding_set, GDK_End, GTK_SCROLL_END); + + g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate)); + +} + +static void modest_header_view_observer_init( + ModestHeaderViewObserverIface *iface_class) +{ + iface_class->update_func = modest_msg_view_window_update_model_replaced; +} + +static void +modest_msg_view_window_init (ModestMsgViewWindow *obj) +{ + ModestMsgViewWindowPrivate *priv; + ModestWindowPrivate *parent_priv = NULL; + GtkActionGroup *action_group = NULL; + GError *error = NULL; + GdkPixbuf *window_icon; + + priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj); + parent_priv = MODEST_WINDOW_GET_PRIVATE(obj); + parent_priv->ui_manager = gtk_ui_manager_new(); + + action_group = gtk_action_group_new ("ModestMsgViewWindowActions"); + gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE); + + /* Add common actions */ + gtk_action_group_add_actions (action_group, + modest_action_entries, + G_N_ELEMENTS (modest_action_entries), + obj); + gtk_action_group_add_toggle_actions (action_group, + modest_toggle_action_entries, + G_N_ELEMENTS (modest_toggle_action_entries), + obj); + gtk_action_group_add_toggle_actions (action_group, + msg_view_toggle_action_entries, + G_N_ELEMENTS (msg_view_toggle_action_entries), + obj); + gtk_action_group_add_radio_actions (action_group, + msg_view_zoom_action_entries, + G_N_ELEMENTS (msg_view_zoom_action_entries), + 100, + G_CALLBACK (modest_ui_actions_on_change_zoom), + obj); + + gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0); + g_object_unref (action_group); + + /* Load the UI definition */ + gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml", + &error); + if (error) { + g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message); + g_error_free (error); + error = NULL; + } + /* ****** */ + + /* Add accelerators */ + gtk_window_add_accel_group (GTK_WINDOW (obj), + gtk_ui_manager_get_accel_group (parent_priv->ui_manager)); + + priv->is_search_result = FALSE; + priv->is_outbox = FALSE; + + priv->msg_view = NULL; + priv->header_model = NULL; + priv->header_folder_id = NULL; + priv->clipboard_change_handler = 0; + priv->queue_change_handler = 0; + priv->account_removed_handler = 0; + priv->row_changed_handler = 0; + priv->row_deleted_handler = 0; + priv->row_inserted_handler = 0; + priv->rows_reordered_handler = 0; + priv->current_toolbar_mode = TOOLBAR_MODE_NORMAL; + + priv->optimized_view = FALSE; + priv->progress_bar_timeout = 0; + priv->purge_timeout = 0; + priv->remove_attachment_banner = NULL; + priv->msg_uid = NULL; + + priv->sighandlers = NULL; + + /* Init window */ + init_window (MODEST_MSG_VIEW_WINDOW(obj)); + + /* Set window icon */ + window_icon = modest_platform_get_icon (MODEST_APP_MSG_VIEW_ICON, MODEST_ICON_SIZE_BIG); + if (window_icon) { + gtk_window_set_icon (GTK_WINDOW (obj), window_icon); + g_object_unref (window_icon); + } + + hildon_program_add_window (hildon_program_get_instance(), + HILDON_WINDOW(obj)); + + modest_window_mgr_register_help_id (modest_runtime_get_window_mgr(), + GTK_WINDOW(obj),"applications_email_viewer"); +} + + +static gboolean +set_toolbar_transfer_mode (ModestMsgViewWindow *self) +{ + ModestMsgViewWindowPrivate *priv = NULL; + + g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE); + + priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self); + + set_toolbar_mode (self, TOOLBAR_MODE_TRANSFER); + + if (priv->progress_bar_timeout > 0) { + g_source_remove (priv->progress_bar_timeout); + priv->progress_bar_timeout = 0; + } + + return FALSE; +} + +static void +set_toolbar_mode (ModestMsgViewWindow *self, + ModestToolBarModes mode) +{ + ModestWindowPrivate *parent_priv; + ModestMsgViewWindowPrivate *priv; +/* GtkWidget *widget = NULL; */ + + g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self)); + + parent_priv = MODEST_WINDOW_GET_PRIVATE(self); + priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self); + + /* Sets current toolbar mode */ + priv->current_toolbar_mode = mode; + + /* Update toolbar dimming state */ + modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self)); + + switch (mode) { + case TOOLBAR_MODE_NORMAL: + if (priv->progress_toolitem) { + gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE); + gtk_widget_hide (priv->progress_toolitem); + } + + if (priv->progress_bar) + gtk_widget_hide (priv->progress_bar); + + if (priv->cancel_toolitem) + gtk_widget_hide (priv->cancel_toolitem); + + if (priv->prev_toolitem) + gtk_widget_show (priv->prev_toolitem); + + if (priv->next_toolitem) + gtk_widget_show (priv->next_toolitem); + + /* Hide toolbar if optimized view is enabled */ + if (priv->optimized_view) { + gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE); + gtk_widget_hide (GTK_WIDGET(parent_priv->toolbar)); + } + + break; + case TOOLBAR_MODE_TRANSFER: + if (priv->prev_toolitem) + gtk_widget_hide (priv->prev_toolitem); + + if (priv->next_toolitem) + gtk_widget_hide (priv->next_toolitem); + + if (priv->progress_bar) + gtk_widget_show (priv->progress_bar); + + if (priv->progress_toolitem) { + gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE); + gtk_widget_show (priv->progress_toolitem); + } + + if (priv->cancel_toolitem) + gtk_widget_show (priv->cancel_toolitem); + + /* Show toolbar if it's hiden (optimized view ) */ + if (priv->optimized_view) { + gtk_widget_set_no_show_all (parent_priv->toolbar, FALSE); + gtk_widget_show (GTK_WIDGET (parent_priv->toolbar)); + } + + break; + default: + g_return_if_reached (); + } + +} + + +static void +init_window (ModestMsgViewWindow *obj) +{ + GtkWidget *main_vbox; + ModestMsgViewWindowPrivate *priv; + + priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj); + + priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ())); + modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE); + main_vbox = gtk_vbox_new (FALSE, 6); + +#ifdef MODEST_USE_MOZEMBED + priv->main_scroll = priv->msg_view; + gtk_widget_set_size_request (priv->msg_view, -1, 1600); +#else + priv->main_scroll = gtk_scrolled_window_new (NULL, NULL); + gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view); +#endif + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_SHADOW_NONE); + modest_maemo_set_thumbable_scrollbar (GTK_SCROLLED_WINDOW(priv->main_scroll), TRUE); + + gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0); + gtk_container_add (GTK_CONTAINER(obj), main_vbox); + + priv->find_toolbar = hildon_find_toolbar_new (NULL); + hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar)); + gtk_widget_set_no_show_all (priv->find_toolbar, TRUE); + + gtk_widget_show_all (GTK_WIDGET(main_vbox)); +} + +static void +modest_msg_view_window_disconnect_signals (ModestWindow *self) +{ + ModestMsgViewWindowPrivate *priv; + ModestHeaderView *header_view = NULL; + ModestWindow *main_window = NULL; + + priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self); + + if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) && + g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY), + priv->clipboard_change_handler)) + g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY), + priv->clipboard_change_handler); + + if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()), + priv->queue_change_handler)) + g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()), + priv->queue_change_handler); + + if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()), + priv->account_removed_handler)) + g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()), + priv->account_removed_handler); + + if (priv->header_model) { + if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), + priv->row_changed_handler)) + g_signal_handler_disconnect(G_OBJECT (priv->header_model), + priv->row_changed_handler); + + if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), + priv->row_deleted_handler)) + g_signal_handler_disconnect(G_OBJECT (priv->header_model), + priv->row_deleted_handler); + + if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), + priv->row_inserted_handler)) + g_signal_handler_disconnect(G_OBJECT (priv->header_model), + priv->row_inserted_handler); + + if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), + priv->rows_reordered_handler)) + g_signal_handler_disconnect(G_OBJECT (priv->header_model), + priv->rows_reordered_handler); + } + + modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers); + priv->sighandlers = NULL; + + main_window = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), + FALSE); /* don't create */ + if (!main_window) + return; + + header_view = MODEST_HEADER_VIEW( + modest_main_window_get_child_widget( + MODEST_MAIN_WINDOW(main_window), + MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW)); + if (header_view == NULL) + return; + + modest_header_view_remove_observer(header_view, + MODEST_HEADER_VIEW_OBSERVER(self)); +} + +static void +modest_msg_view_window_finalize (GObject *obj) +{ + ModestMsgViewWindowPrivate *priv; + + priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj); + + /* Sanity check: shouldn't be needed, the window mgr should + call this function before */ + modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj)); + + if (priv->header_model != NULL) { + g_object_unref (priv->header_model); + priv->header_model = NULL; + } + + if (priv->progress_bar_timeout > 0) { + g_source_remove (priv->progress_bar_timeout); + priv->progress_bar_timeout = 0; + } + + if (priv->remove_attachment_banner) { + gtk_widget_destroy (priv->remove_attachment_banner); + g_object_unref (priv->remove_attachment_banner); + priv->remove_attachment_banner = NULL; + } + + if (priv->purge_timeout > 0) { + g_source_remove (priv->purge_timeout); + priv->purge_timeout = 0; + } + + if (priv->row_reference) { + gtk_tree_row_reference_free (priv->row_reference); + priv->row_reference = NULL; + } + + if (priv->next_row_reference) { + gtk_tree_row_reference_free (priv->next_row_reference); + priv->next_row_reference = NULL; + } + + if (priv->msg_uid) { + g_free (priv->msg_uid); + priv->msg_uid = NULL; + } + + G_OBJECT_CLASS(parent_class)->finalize (obj); +} + +static gboolean +select_next_valid_row (GtkTreeModel *model, + GtkTreeRowReference **row_reference, + gboolean cycle, + gboolean is_outbox) +{ + GtkTreeIter tmp_iter; + GtkTreePath *path; + GtkTreePath *next = NULL; + gboolean retval = FALSE, finished; + + g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE); + + path = gtk_tree_row_reference_get_path (*row_reference); + gtk_tree_model_get_iter (model, &tmp_iter, path); + gtk_tree_row_reference_free (*row_reference); + *row_reference = NULL; + + finished = FALSE; + do { + TnyHeader *header = NULL; + + if (gtk_tree_model_iter_next (model, &tmp_iter)) { + gtk_tree_model_get (model, &tmp_iter, + TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, + &header, -1); + + if (header) { + if (msg_is_visible (header, is_outbox)) { + next = gtk_tree_model_get_path (model, &tmp_iter); + *row_reference = gtk_tree_row_reference_new (model, next); + retval = TRUE; + finished = TRUE; + } + g_object_unref (header); + header = NULL; + } + } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) { + next = gtk_tree_model_get_path (model, &tmp_iter); + + /* Ensure that we are not selecting the same */ + if (gtk_tree_path_compare (path, next) != 0) { + gtk_tree_model_get (model, &tmp_iter, + TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, + &header, -1); + if (header) { + if (msg_is_visible (header, is_outbox)) { + *row_reference = gtk_tree_row_reference_new (model, next); + retval = TRUE; + finished = TRUE; + } + g_object_unref (header); + header = NULL; + } + } else { + /* If we ended up in the same message + then there is no valid next + message */ + finished = TRUE; + } + } else { + /* If there are no more messages and we don't + want to start again in the first one then + there is no valid next message */ + finished = TRUE; + } + } while (!finished); + + /* Free */ + gtk_tree_path_free (path); + if (next) + gtk_tree_path_free (next); + + return retval; +} + +/* TODO: This should be in _init(), with the parameters as properties. */ +static void +modest_msg_view_window_construct (ModestMsgViewWindow *self, + const gchar *modest_account_name, + const gchar *msg_uid) +{ + GObject *obj = NULL; + ModestMsgViewWindowPrivate *priv = NULL; + ModestWindowPrivate *parent_priv = NULL; + ModestDimmingRulesGroup *menu_rules_group = NULL; + ModestDimmingRulesGroup *toolbar_rules_group = NULL; + ModestDimmingRulesGroup *clipboard_rules_group = NULL; + + obj = G_OBJECT (self); + priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj); + parent_priv = MODEST_WINDOW_GET_PRIVATE(obj); + + priv->msg_uid = g_strdup (msg_uid); + + /* Menubar */ + parent_priv->menubar = modest_maemo_utils_get_manager_menubar_as_menu (parent_priv->ui_manager, "/MenuBar"); + hildon_window_set_menu (HILDON_WINDOW(obj), GTK_MENU(parent_priv->menubar)); + gtk_widget_show (parent_priv->menubar); + parent_priv->ui_dimming_manager = modest_ui_dimming_manager_new(); + + menu_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_MENU, FALSE); + toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE); + clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE); + + /* Add common dimming rules */ + modest_dimming_rules_group_add_rules (menu_rules_group, + modest_msg_view_menu_dimming_entries, + G_N_ELEMENTS (modest_msg_view_menu_dimming_entries), + MODEST_WINDOW (self)); + modest_dimming_rules_group_add_rules (toolbar_rules_group, + modest_msg_view_toolbar_dimming_entries, + G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries), + MODEST_WINDOW (self)); + modest_dimming_rules_group_add_rules (clipboard_rules_group, + modest_msg_view_clipboard_dimming_entries, + G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries), + MODEST_WINDOW (self)); + + /* Insert dimming rules group for this window */ + modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, menu_rules_group); + modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group); + modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group); + g_object_unref (menu_rules_group); + g_object_unref (toolbar_rules_group); + g_object_unref (clipboard_rules_group); + + restore_settings (MODEST_MSG_VIEW_WINDOW(obj)); + + /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */ + + priv->clipboard_change_handler = g_signal_connect (G_OBJECT (gtk_clipboard_get (GDK_SELECTION_PRIMARY)), "owner-change", G_CALLBACK (modest_msg_view_window_clipboard_owner_change), obj); + g_signal_connect (G_OBJECT(priv->msg_view), "activate_link", + G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj); + g_signal_connect (G_OBJECT(priv->msg_view), "link_hover", + G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj); + g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked", + G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj); + g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated", + G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj); + g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual", + G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj); + g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image", + G_CALLBACK (on_fetch_image), obj); + + g_signal_connect (G_OBJECT (obj), "key-release-event", + G_CALLBACK (modest_msg_view_window_key_event), + NULL); + + g_signal_connect (G_OBJECT (obj), "key-press-event", + G_CALLBACK (modest_msg_view_window_key_event), + NULL); + + g_signal_connect (G_OBJECT (obj), "window-state-event", + G_CALLBACK (modest_msg_view_window_window_state_event), + NULL); + + g_signal_connect (G_OBJECT (obj), "move-focus", + G_CALLBACK (on_move_focus), obj); + + /* Mail Operation Queue */ + priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()), + "queue-changed", + G_CALLBACK (on_queue_changed), + obj); + + /* Account manager */ + priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()), + "account_removed", + G_CALLBACK(on_account_removed), + obj); + + modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name); + + g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj); + g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj); + priv->last_search = NULL; + + /* Init the clipboard actions dim status */ + modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view)); + + update_window_title (MODEST_MSG_VIEW_WINDOW (obj)); + + +} + +/* FIXME: parameter checks */ +ModestWindow * +modest_msg_view_window_new_with_header_model (TnyMsg *msg, + const gchar *modest_account_name, + const gchar *msg_uid, + GtkTreeModel *model, + GtkTreeRowReference *row_reference) +{ + ModestMsgViewWindow *window = NULL; + ModestMsgViewWindowPrivate *priv = NULL; + TnyFolder *header_folder = NULL; + ModestHeaderView *header_view = NULL; + ModestWindow *main_window = NULL; + ModestWindowMgr *mgr = NULL; + + MODEST_DEBUG_BLOCK ( + modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0); + ); + + mgr = modest_runtime_get_window_mgr (); + window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr)); + g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL); + + modest_msg_view_window_construct (window, modest_account_name, msg_uid); + + priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); + + /* Remember the message list's TreeModel so we can detect changes + * and change the list selection when necessary: */ + + main_window = modest_window_mgr_get_main_window(mgr, FALSE); /* don't create */ + if (main_window) { + header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget( + MODEST_MAIN_WINDOW(main_window), + MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW)); + } + + if (header_view != NULL){ + header_folder = modest_header_view_get_folder(header_view); + /* This could happen if the header folder was + unseleted before opening this msg window (for + example if the user selects an account in the + folder view of the main window */ + if (header_folder) { + priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) == TNY_FOLDER_TYPE_OUTBOX); + priv->header_folder_id = tny_folder_get_id(header_folder); + g_assert(priv->header_folder_id != NULL); + g_object_unref(header_folder); + } + } + + /* Setup row references and connect signals */ + priv->header_model = g_object_ref (model); + + if (row_reference) { + priv->row_reference = gtk_tree_row_reference_copy (row_reference); + priv->next_row_reference = gtk_tree_row_reference_copy (row_reference); + select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox); + } else { + priv->row_reference = NULL; + priv->next_row_reference = NULL; + } + + /* Connect signals */ + priv->row_changed_handler = + g_signal_connect (GTK_TREE_MODEL(model), "row-changed", + G_CALLBACK(modest_msg_view_window_on_row_changed), + window); + priv->row_deleted_handler = + g_signal_connect (GTK_TREE_MODEL(model), "row-deleted", + G_CALLBACK(modest_msg_view_window_on_row_deleted), + window); + priv->row_inserted_handler = + g_signal_connect (GTK_TREE_MODEL(model), "row-inserted", + G_CALLBACK(modest_msg_view_window_on_row_inserted), + window); + priv->rows_reordered_handler = + g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered", + G_CALLBACK(modest_msg_view_window_on_row_reordered), + window); + + if (header_view != NULL){ + modest_header_view_add_observer(header_view, + MODEST_HEADER_VIEW_OBSERVER(window)); + } + + tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg); + update_window_title (MODEST_MSG_VIEW_WINDOW (window)); + gtk_widget_show_all (GTK_WIDGET (window)); + modest_msg_view_window_update_priority (window); + + /* Check dimming rules */ + modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window)); + modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window)); + modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD); + + return MODEST_WINDOW(window); +} + +ModestWindow * +modest_msg_view_window_new_for_search_result (TnyMsg *msg, + const gchar *modest_account_name, + const gchar *msg_uid) +{ + ModestMsgViewWindow *window = NULL; + ModestMsgViewWindowPrivate *priv = NULL; + ModestWindowMgr *mgr = NULL; + + mgr = modest_runtime_get_window_mgr (); + window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr)); + g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL); + modest_msg_view_window_construct (window, modest_account_name, msg_uid); + + priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); + + /* Remember that this is a search result, + * so we can disable some UI appropriately: */ + priv->is_search_result = TRUE; + + tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg); + + update_window_title (window); + gtk_widget_show_all (GTK_WIDGET (window)); + modest_msg_view_window_update_priority (window); + + /* Check dimming rules */ + modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window)); + modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window)); + modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD); + + return MODEST_WINDOW(window); +} + +ModestWindow * +modest_msg_view_window_new_for_attachment (TnyMsg *msg, + const gchar *modest_account_name, + const gchar *msg_uid) +{ + GObject *obj = NULL; + ModestMsgViewWindowPrivate *priv; + ModestWindowMgr *mgr = NULL; + + g_return_val_if_fail (msg, NULL); + mgr = modest_runtime_get_window_mgr (); + obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr)); + priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj); + modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj), + modest_account_name, msg_uid); + + tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg); + update_window_title (MODEST_MSG_VIEW_WINDOW (obj)); + + gtk_widget_show_all (GTK_WIDGET (obj)); + + /* Check dimming rules */ + modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj)); + modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj)); + modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD); + + return MODEST_WINDOW(obj); +} + +static void +modest_msg_view_window_on_row_changed (GtkTreeModel *header_model, + GtkTreePath *arg1, + GtkTreeIter *arg2, + ModestMsgViewWindow *window) +{ + check_dimming_rules_after_change (window); +} + +static void +modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model, + GtkTreePath *arg1, + ModestMsgViewWindow *window) +{ + check_dimming_rules_after_change (window); +} + /* The window could have dissapeared */ + +static void +check_dimming_rules_after_change (ModestMsgViewWindow *window) +{ + modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window)); + modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window)); +} + + +/* On insertions we check if the folder still has the message we are + * showing or do not. If do not, we do nothing. Which means we are still + * not attached to any header folder and thus next/prev buttons are + * still dimmed. Once the message that is shown by msg-view is found, the + * new model of header-view will be attached and the references will be set. + * On each further insertions dimming rules will be checked. However + * this requires extra CPU time at least works. + * (An message might be deleted from TnyFolder and thus will not be + * inserted into the model again for example if it is removed by the + * imap server and the header view is refreshed.) + */ +static void +modest_msg_view_window_on_row_inserted (GtkTreeModel *model, + GtkTreePath *tree_path, + GtkTreeIter *tree_iter, + ModestMsgViewWindow *window) +{ + ModestMsgViewWindowPrivate *priv = NULL; + TnyHeader *header = NULL; + + g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window)); + priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); + + g_assert (model == priv->header_model); + + /* Check if the newly inserted message is the same we are actually + * showing. IF not, we should remain detached from the header model + * and thus prev and next toolbar buttons should remain dimmed. */ + gtk_tree_model_get (model, tree_iter, + TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, + &header, -1); + + if (TNY_IS_HEADER (header)) { + gchar *uid = NULL; + + uid = modest_tny_folder_get_header_unique_id (header); + if (!g_str_equal(priv->msg_uid, uid)) { + check_dimming_rules_after_change (window); + g_free(uid); + g_object_unref (G_OBJECT(header)); + return; + } + g_free(uid); + g_object_unref(G_OBJECT(header)); + } + + if (priv->row_reference) { + gtk_tree_row_reference_free (priv->row_reference); + } + + /* Setup row_reference for the actual msg. */ + priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path); + if (priv->row_reference == NULL) { + g_warning("No reference for msg header item."); + return; + } + + /* Now set up next_row_reference. */ + if (priv->next_row_reference) { + gtk_tree_row_reference_free (priv->next_row_reference); + } + + priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference); + select_next_valid_row (priv->header_model, + &(priv->next_row_reference), FALSE, priv->is_outbox); + + /* Connect the remaining callbacks to become able to detect + * changes in header-view. */ + priv->row_changed_handler = + g_signal_connect (priv->header_model, "row-changed", + G_CALLBACK (modest_msg_view_window_on_row_changed), + window); + priv->row_deleted_handler = + g_signal_connect (priv->header_model, "row-deleted", + G_CALLBACK (modest_msg_view_window_on_row_deleted), + window); + priv->rows_reordered_handler = + g_signal_connect (priv->header_model, "rows-reordered", + G_CALLBACK (modest_msg_view_window_on_row_reordered), + window); + + check_dimming_rules_after_change (window); +} + +static void +modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model, + GtkTreePath *arg1, + GtkTreeIter *arg2, + gpointer arg3, + ModestMsgViewWindow *window) +{ + ModestMsgViewWindowPrivate *priv = NULL; + gboolean already_changed = FALSE; + + priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window); + + /* If the current row was reordered select the proper next + valid row. The same if the next row reference changes */ + if (priv->row_reference && + gtk_tree_row_reference_valid (priv->row_reference)) { + GtkTreePath *path; + path = gtk_tree_row_reference_get_path (priv->row_reference); + if (gtk_tree_path_compare (path, arg1) == 0) { + if (priv->next_row_reference) { + gtk_tree_row_reference_free (priv->next_row_reference); + } + priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference); + select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox); + already_changed = TRUE; + } + gtk_tree_path_free (path); + } + if (!already_changed && + priv->next_row_reference && + gtk_tree_row_reference_valid (priv->next_row_reference)) { + GtkTreePath *path; + path = gtk_tree_row_reference_get_path (priv->next_row_reference); + if (gtk_tree_path_compare (path, arg1) == 0) { + if (priv->next_row_reference) { + gtk_tree_row_reference_free (priv->next_row_reference); + } + priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference); + select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox); + } + gtk_tree_path_free (path); + } + check_dimming_rules_after_change (window); +} + +/* The modest_msg_view_window_update_model_replaced implements update + * function for ModestHeaderViewObserver. Checks whether the TnyFolder + * actually belongs to the header-view is the same as the TnyFolder of + * the message of msg-view or not. If they are different, there is + * nothing to do. If they are the same, then the model has replaced and + * the reference in msg-view shall be replaced from the old model to + * the new model. In this case the view will be detached from it's + * header folder. From this point the next/prev buttons are dimmed. + */ +static void +modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer, + GtkTreeModel *model, + const gchar *tny_folder_id) +{ + ModestMsgViewWindowPrivate *priv = NULL; + ModestMsgViewWindow *window = NULL; + + g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer)); + g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer)); + + window = MODEST_MSG_VIEW_WINDOW(observer); + priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window); + + /* If there is an other folder in the header-view then we do + * not care about it's model (msg list). Else if the + * header-view shows the folder the msg shown by us is in, we + * shall replace our model reference and make some check. */ + if(model == NULL || tny_folder_id == NULL || + (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id))) + return; + + /* Model is changed(replaced), so we should forget the old + * one. Because there might be other references and there + * might be some change on the model even if we unreferenced + * it, we need to disconnect our signals here. */ + if (priv->header_model) { + if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), + priv->row_changed_handler)) + g_signal_handler_disconnect(G_OBJECT (priv->header_model), + priv->row_changed_handler); + if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), + priv->row_deleted_handler)) + g_signal_handler_disconnect(G_OBJECT (priv->header_model), + priv->row_deleted_handler); + if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), + priv->row_inserted_handler)) + g_signal_handler_disconnect(G_OBJECT (priv->header_model), + priv->row_inserted_handler); + if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), + priv->rows_reordered_handler)) + g_signal_handler_disconnect(G_OBJECT (priv->header_model), + priv->rows_reordered_handler); + + /* Frees */ + if (priv->row_reference) + gtk_tree_row_reference_free (priv->row_reference); + if (priv->next_row_reference) + gtk_tree_row_reference_free (priv->next_row_reference); + g_object_unref(priv->header_model); + + /* Initialize */ + priv->row_changed_handler = 0; + priv->row_deleted_handler = 0; + priv->row_inserted_handler = 0; + priv->rows_reordered_handler = 0; + priv->next_row_reference = NULL; + priv->row_reference = NULL; + priv->header_model = NULL; + } + + priv->header_model = g_object_ref (model); + + /* Also we must connect to the new model for row insertions. + * Only for insertions now. We will need other ones only after + * the msg is show by msg-view is added to the new model. */ + priv->row_inserted_handler = + g_signal_connect (priv->header_model, "row-inserted", + G_CALLBACK(modest_msg_view_window_on_row_inserted), + window); + + modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window)); + modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window)); +} + +gboolean +modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self) +{ + ModestMsgViewWindowPrivate *priv= NULL; + + g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE); + priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self); + + return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER; +} + +TnyHeader* +modest_msg_view_window_get_header (ModestMsgViewWindow *self) +{ + ModestMsgViewWindowPrivate *priv= NULL; + TnyMsg *msg = NULL; + TnyHeader *header = NULL; + GtkTreePath *path = NULL; + GtkTreeIter iter; + + g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL); + priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self); + + /* If the message was not obtained from a treemodel, + * for instance if it was opened directly by the search UI: + */ + if (priv->header_model == NULL || + priv->row_reference == NULL || + !gtk_tree_row_reference_valid (priv->row_reference)) { + msg = modest_msg_view_window_get_message (self); + if (msg) { + header = tny_msg_get_header (msg); + g_object_unref (msg); + } + return header; + } + + /* Get iter of the currently selected message in the header view: */ + path = gtk_tree_row_reference_get_path (priv->row_reference); + g_return_val_if_fail (path != NULL, NULL); + gtk_tree_model_get_iter (priv->header_model, + &iter, + path); + + /* Get current message header */ + gtk_tree_model_get (priv->header_model, &iter, + TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, + &header, -1); + + gtk_tree_path_free (path); + return header; +} + +TnyMsg* +modest_msg_view_window_get_message (ModestMsgViewWindow *self) +{ + ModestMsgViewWindowPrivate *priv; + + g_return_val_if_fail (self, NULL); + + priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self); + + return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view)); +} + +const gchar* +modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self) +{ + ModestMsgViewWindowPrivate *priv; + + g_return_val_if_fail (self, NULL); + + priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self); + + return (const gchar*) priv->msg_uid; +} + +static void +modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle, + gpointer data) +{ + ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data); + ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); + ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window); + gboolean is_active; + GtkAction *action; + + is_active = gtk_toggle_action_get_active (toggle); + + if (is_active) { + gtk_widget_show (priv->find_toolbar); + hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE); + } else { + gtk_widget_hide (priv->find_toolbar); + } + + /* update the toggle buttons status */ + action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"); + modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active); + action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ToolsMenu/ToolsFindInMessageMenu"); + modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active); + +} + +static void +modest_msg_view_window_find_toolbar_close (GtkWidget *widget, + ModestMsgViewWindow *obj) +{ + GtkToggleAction *toggle; + ModestWindowPrivate *parent_priv; + ModestMsgViewWindowPrivate *priv; + + priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj); + parent_priv = MODEST_WINDOW_GET_PRIVATE (obj); + + toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage")); + gtk_toggle_action_set_active (toggle, FALSE); + modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view)); +} + +static void +modest_msg_view_window_find_toolbar_search (GtkWidget *widget, + ModestMsgViewWindow *obj) +{ + gchar *current_search; + ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj); + + if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) { + hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find")); + return; + } + + g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL); + + if ((current_search == NULL) || (strcmp (current_search, "") == 0)) { + g_free (current_search); + hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ecdg_ib_find_rep_enter_text")); + return; + } + + if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) { + gboolean result; + g_free (priv->last_search); + priv->last_search = g_strdup (current_search); + result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view), + priv->last_search); + if (!result) { + hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_no_matches")); + g_free (priv->last_search); + priv->last_search = NULL; + } else { + modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view)); + hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE); + } + } else { + if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) { + hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_search_complete")); + g_free (priv->last_search); + priv->last_search = NULL; + } else { + modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view)); + hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE); + } + } + + g_free (current_search); + +} + +static void +modest_msg_view_window_set_zoom (ModestWindow *window, + gdouble zoom) +{ + ModestMsgViewWindowPrivate *priv; + ModestWindowPrivate *parent_priv; + GtkAction *action = NULL; + gint int_zoom = (gint) rint (zoom*100.0+0.1); + + g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window)); + + priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); + parent_priv = MODEST_WINDOW_GET_PRIVATE (window); + modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom); + + action = gtk_ui_manager_get_action (parent_priv->ui_manager, + "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"); + + gtk_radio_action_set_current_value (GTK_RADIO_ACTION (action), int_zoom); +} + +static gdouble +modest_msg_view_window_get_zoom (ModestWindow *window) +{ + ModestMsgViewWindowPrivate *priv; + + g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0); + + priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); + return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view)); +} + +static gboolean +modest_msg_view_window_zoom_plus (ModestWindow *window) +{ + ModestWindowPrivate *parent_priv; + GtkRadioAction *zoom_radio_action; + GSList *group, *node; + + parent_priv = MODEST_WINDOW_GET_PRIVATE (window); + zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, + "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu")); + + group = gtk_radio_action_get_group (zoom_radio_action); + + if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (group->data))) { + hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_max_zoom_level_reached")); + return FALSE; + } + + for (node = group; node != NULL; node = g_slist_next (node)) { + if ((node->next != NULL) && gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->next->data))) { + gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->data), TRUE); + return TRUE; + } + } + return FALSE; +} + +static gboolean +modest_msg_view_window_zoom_minus (ModestWindow *window) +{ + ModestWindowPrivate *parent_priv; + GtkRadioAction *zoom_radio_action; + GSList *group, *node; + + parent_priv = MODEST_WINDOW_GET_PRIVATE (window); + zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, + "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu")); + + group = gtk_radio_action_get_group (zoom_radio_action); + + for (node = group; node != NULL; node = g_slist_next (node)) { + if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->data))) { + if (node->next != NULL) { + gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->next->data), TRUE); + return TRUE; + } else { + hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_min_zoom_level_reached")); + return FALSE; + } + break; + } + } + return FALSE; +} + +static gboolean +modest_msg_view_window_key_event (GtkWidget *window, + GdkEventKey *event, + gpointer userdata) +{ + GtkWidget *focus; + + focus = gtk_window_get_focus (GTK_WINDOW (window)); + + /* for the find toolbar case */ + if (focus && GTK_IS_ENTRY (focus)) { + if (event->keyval == GDK_BackSpace) { + GdkEvent *copy; + copy = gdk_event_copy ((GdkEvent *) event); + gtk_widget_event (focus, copy); + gdk_event_free (copy); + return TRUE; + } else + return FALSE; + } + if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up || + event->keyval == GDK_Down || event->keyval == GDK_KP_Down || + event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up || + event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down || + event->keyval == GDK_Home || event->keyval == GDK_KP_Home || + event->keyval == GDK_End || event->keyval == GDK_KP_End) { + /* ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); */ + /* gboolean return_value; */ + + if (event->type == GDK_KEY_PRESS) { + GtkScrollType scroll_type; + + switch (event->keyval) { + case GDK_Up: + case GDK_KP_Up: + scroll_type = GTK_SCROLL_STEP_UP; break; + case GDK_Down: + case GDK_KP_Down: + scroll_type = GTK_SCROLL_STEP_DOWN; break; + case GDK_Page_Up: + case GDK_KP_Page_Up: + scroll_type = GTK_SCROLL_PAGE_UP; break; + case GDK_Page_Down: + case GDK_KP_Page_Down: + scroll_type = GTK_SCROLL_PAGE_DOWN; break; + case GDK_Home: + case GDK_KP_Home: + scroll_type = GTK_SCROLL_START; break; + case GDK_End: + case GDK_KP_End: + scroll_type = GTK_SCROLL_END; break; + default: scroll_type = GTK_SCROLL_NONE; + } + + /* g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", */ + /* scroll_type, FALSE, &return_value); */ + return FALSE; + } else { + return FALSE; + } + } else { + return FALSE; + } +} + +gboolean +modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window) +{ + GtkTreePath *path; + ModestMsgViewWindowPrivate *priv; + GtkTreeIter tmp_iter; + gboolean is_last_selected; + + g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE); + priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); + + /*if no model (so no rows at all), then virtually we are the last*/ + if (!priv->header_model || !priv->row_reference) + return TRUE; + + if (!gtk_tree_row_reference_valid (priv->row_reference)) + return TRUE; + + path = gtk_tree_row_reference_get_path (priv->row_reference); + if (path == NULL) + return TRUE; + + is_last_selected = TRUE; + while (is_last_selected) { + TnyHeader *header; + gtk_tree_path_next (path); + if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path)) + break; + gtk_tree_model_get (priv->header_model, &tmp_iter, + TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, + &header, -1); + if (header) { + if (msg_is_visible (header, priv->is_outbox)) + is_last_selected = FALSE; + g_object_unref(G_OBJECT(header)); + } + } + gtk_tree_path_free (path); + return is_last_selected; +} + +gboolean +modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window) +{ + ModestMsgViewWindowPrivate *priv; + + g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE); + priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); + + return priv->header_model != NULL; +} + +gboolean +modest_msg_view_window_is_search_result (ModestMsgViewWindow *window) +{ + ModestMsgViewWindowPrivate *priv; + + g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE); + priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); + + return priv->is_search_result; +} + +static gboolean +msg_is_visible (TnyHeader *header, gboolean check_outbox) +{ + if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED)) + return FALSE; + if (!check_outbox) { + return TRUE; + } else { + ModestTnySendQueueStatus status; + status = modest_tny_all_send_queues_get_msg_status (header); + return ((status != MODEST_TNY_SEND_QUEUE_FAILED) && + (status != MODEST_TNY_SEND_QUEUE_SENDING)); + } +} + +gboolean +modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window) +{ + GtkTreePath *path; + ModestMsgViewWindowPrivate *priv; + gboolean is_first_selected; + GtkTreeIter tmp_iter; + + g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE); + priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); + + /*if no model (so no rows at all), then virtually we are the first*/ + if (!priv->header_model || !priv->row_reference) + return TRUE; + + if (!gtk_tree_row_reference_valid (priv->row_reference)) + return TRUE; + + path = gtk_tree_row_reference_get_path (priv->row_reference); + if (!path) + return TRUE; + + is_first_selected = TRUE; + while (is_first_selected) { + TnyHeader *header; + if(!gtk_tree_path_prev (path)) + break; + /* Here the 'if' is needless for logic, but let make sure + * iter is valid for gtk_tree_model_get. */ + if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path)) + break; + gtk_tree_model_get (priv->header_model, &tmp_iter, + TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, + &header, -1); + if (header) { + if (msg_is_visible (header, priv->is_outbox)) + is_first_selected = FALSE; + g_object_unref(G_OBJECT(header)); + } + } + gtk_tree_path_free (path); + return is_first_selected; +} + +typedef struct { + TnyHeader *header; + GtkTreeRowReference *row_reference; +} MsgReaderInfo; + +static void +message_reader_performer (gboolean canceled, + GError *err, + GtkWindow *parent_window, + TnyAccount *account, + gpointer user_data) +{ + ModestMailOperation *mail_op = NULL; + MsgReaderInfo *info; + + info = (MsgReaderInfo *) user_data; + if (canceled || err) { + goto frees; + } + + /* Register the header - it'll be unregistered in the callback */ + modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL); + + /* New mail operation */ + mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window), + modest_ui_actions_disk_operations_error_handler, + NULL, NULL); + + modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op); + modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference); + g_object_unref (mail_op); + + /* Update dimming rules */ + modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window)); + modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window)); + + frees: + /* Frees. The row_reference will be freed by the view_msg_cb callback */ + g_object_unref (info->header); + g_slice_free (MsgReaderInfo, info); +} + + +/** + * Reads the message whose summary item is @header. It takes care of + * several things, among others: + * + * If the message was not previously downloaded then ask the user + * before downloading. If there is no connection launch the connection + * dialog. Update toolbar dimming rules. + * + * Returns: TRUE if the mail operation was started, otherwise if the + * user do not want to download the message, or if the user do not + * want to connect, then the operation is not issued + **/ +static gboolean +message_reader (ModestMsgViewWindow *window, + ModestMsgViewWindowPrivate *priv, + TnyHeader *header, + GtkTreeRowReference *row_reference) +{ + gboolean already_showing = FALSE; + ModestWindow *msg_window = NULL; + ModestWindowMgr *mgr; + TnyAccount *account; + TnyFolder *folder; + MsgReaderInfo *info; + + g_return_val_if_fail (row_reference != NULL, FALSE); + + mgr = modest_runtime_get_window_mgr (); + already_showing = modest_window_mgr_find_registered_header (mgr, header, &msg_window); + if (already_showing && (msg_window != MODEST_WINDOW (window))) { + gboolean retval; + if (msg_window) + gtk_window_present (GTK_WINDOW (msg_window)); + g_signal_emit_by_name (G_OBJECT (window), "delete-event", NULL, &retval); + return TRUE; + } + + /* Msg download completed */ + if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) { + /* Ask the user if he wants to download the message if + we're not online */ + if (!tny_device_is_online (modest_runtime_get_device())) { + GtkResponseType response; + + response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window), + _("mcen_nc_get_msg")); + if (response == GTK_RESPONSE_CANCEL) + return FALSE; + + folder = tny_header_get_folder (header); + info = g_slice_new (MsgReaderInfo); + info->header = g_object_ref (header); + info->row_reference = gtk_tree_row_reference_copy (row_reference); + + /* Offer the connection dialog if necessary */ + modest_platform_connect_if_remote_and_perform ((GtkWindow *) window, + TRUE, + TNY_FOLDER_STORE (folder), + message_reader_performer, + info); + g_object_unref (folder); + return TRUE; + } + } + + folder = tny_header_get_folder (header); + account = tny_folder_get_account (folder); + info = g_slice_new (MsgReaderInfo); + info->header = g_object_ref (header); + info->row_reference = gtk_tree_row_reference_copy (row_reference); + + message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info); + g_object_unref (account); + g_object_unref (folder); + + return TRUE; +} + +gboolean +modest_msg_view_window_select_next_message (ModestMsgViewWindow *window) +{ + ModestMsgViewWindowPrivate *priv; + GtkTreePath *path= NULL; + GtkTreeIter tmp_iter; + TnyHeader *header; + gboolean retval = TRUE; + GtkTreeRowReference *row_reference = NULL; + + g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE); + priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); + + if (!priv->row_reference) + return FALSE; + + /* Update the next row reference if it's not valid. This could + happen if for example the header which it was pointing to, + was deleted. The best place to do it is in the row-deleted + handler but the tinymail model do not work like the glib + tree models and reports the deletion when the row is still + there */ + if (!gtk_tree_row_reference_valid (priv->next_row_reference)) { + if (gtk_tree_row_reference_valid (priv->row_reference)) { + priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference); + select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox); + } + } + if (priv->next_row_reference) + path = gtk_tree_row_reference_get_path (priv->next_row_reference); + if (path == NULL) + return FALSE; + + row_reference = gtk_tree_row_reference_copy (priv->next_row_reference); + + gtk_tree_model_get_iter (priv->header_model, + &tmp_iter, + path); + gtk_tree_path_free (path); + + gtk_tree_model_get (priv->header_model, &tmp_iter, + TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, + &header, -1); + + /* Read the message & show it */ + if (!message_reader (window, priv, header, row_reference)) { + retval = FALSE; + } + gtk_tree_row_reference_free (row_reference); + + /* Free */ + g_object_unref (header); + + return retval; +} + +gboolean +modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window) +{ + ModestMsgViewWindowPrivate *priv = NULL; + GtkTreePath *path; + gboolean finished = FALSE; + gboolean retval = FALSE; + + g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE); + priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); + + /* Return inmediatly if there is no header model */ + if (!priv->header_model || !priv->row_reference) + return FALSE; + + path = gtk_tree_row_reference_get_path (priv->row_reference); + while (!finished && gtk_tree_path_prev (path)) { + TnyHeader *header; + GtkTreeIter iter; + + gtk_tree_model_get_iter (priv->header_model, &iter, path); + gtk_tree_model_get (priv->header_model, &iter, + TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, + &header, -1); + finished = TRUE; + if (header) { + if (msg_is_visible (header, priv->is_outbox)) { + GtkTreeRowReference *row_reference; + row_reference = gtk_tree_row_reference_new (priv->header_model, path); + /* Read the message & show it */ + retval = message_reader (window, priv, header, row_reference); + gtk_tree_row_reference_free (row_reference); + } else { + finished = FALSE; + } + g_object_unref (header); + } + } + + gtk_tree_path_free (path); + return retval; +} + +static void +view_msg_cb (ModestMailOperation *mail_op, + TnyHeader *header, + gboolean canceled, + TnyMsg *msg, + GError *error, + gpointer user_data) +{ + ModestMsgViewWindow *self = NULL; + ModestMsgViewWindowPrivate *priv = NULL; + GtkTreeRowReference *row_reference = NULL; + + /* Unregister the header (it was registered before creating the mail operation) */ + modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header); + + row_reference = (GtkTreeRowReference *) user_data; + if (canceled) { + gtk_tree_row_reference_free (row_reference); + return; + } + + /* If there was any error */ + if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) { + gtk_tree_row_reference_free (row_reference); + return; + } + + /* Get the window */ + self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op); + g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self)); + priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self); + + /* Update the row reference */ + if (priv->row_reference != NULL) { + gtk_tree_row_reference_free (priv->row_reference); + priv->row_reference = gtk_tree_row_reference_copy (row_reference); + if (priv->next_row_reference != NULL) { + gtk_tree_row_reference_free (priv->next_row_reference); + } + priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference); + select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox); + } + + /* Mark header as read */ + if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN)) + tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN); + + /* Set new message */ + if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) { + tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg); + modest_msg_view_window_update_priority (self); + update_window_title (MODEST_MSG_VIEW_WINDOW (self)); + modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view)); + } + + /* Set the new message uid of the window */ + if (priv->msg_uid) { + g_free (priv->msg_uid); + priv->msg_uid = modest_tny_folder_get_header_unique_id (header); + } + + /* Notify the observers */ + g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL], + 0, priv->header_model, priv->row_reference); + + /* Frees */ + g_object_unref (self); + gtk_tree_row_reference_free (row_reference); +} + +TnyFolderType +modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window) +{ + ModestMsgViewWindowPrivate *priv; + TnyMsg *msg; + TnyFolderType folder_type; + + priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); + + folder_type = TNY_FOLDER_TYPE_UNKNOWN; + + msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view)); + if (msg) { + TnyFolder *folder; + + folder = tny_msg_get_folder (msg); + if (folder) { + folder_type = modest_tny_folder_guess_folder_type (folder); + g_object_unref (folder); + } + g_object_unref (msg); + } + + return folder_type; +} + + +static void +modest_msg_view_window_update_priority (ModestMsgViewWindow *window) +{ + ModestMsgViewWindowPrivate *priv; + TnyHeader *header = NULL; + TnyHeaderFlags flags = 0; + + priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); + + if (priv->header_model && priv->row_reference) { + GtkTreeIter iter; + GtkTreePath *path = NULL; + + path = gtk_tree_row_reference_get_path (priv->row_reference); + g_return_if_fail (path != NULL); + gtk_tree_model_get_iter (priv->header_model, + &iter, + gtk_tree_row_reference_get_path (priv->row_reference)); + + gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, + &header, -1); + gtk_tree_path_free (path); + } else { + TnyMsg *msg; + msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view)); + if (msg) { + header = tny_msg_get_header (msg); + g_object_unref (msg); + } + } + + if (header) { + flags = tny_header_get_flags (header); + g_object_unref(G_OBJECT(header)); + } + + modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags); + +} + +static void +toolbar_resize (ModestMsgViewWindow *self) +{ + ModestMsgViewWindowPrivate *priv = NULL; + ModestWindowPrivate *parent_priv = NULL; + GtkWidget *widget; + gint static_button_size; + ModestWindowMgr *mgr; + + g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self)); + priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self); + parent_priv = MODEST_WINDOW_GET_PRIVATE(self); + + mgr = modest_runtime_get_window_mgr (); + static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?118:108; + + if (parent_priv->toolbar) { + /* left size buttons */ + widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply"); + gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE); + gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE); + gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1); + widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo"); + gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE); + gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE); + gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1); + widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage"); + gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE); + gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE); + gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1); + widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FindInMessage"); + gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE); + gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE); + gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1); + + gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE); + gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE); + gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE); + gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE); + gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE); + gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE); + gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE); + gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE); + } + +} + +static gboolean +modest_msg_view_window_window_state_event (GtkWidget *widget, GdkEventWindowState *event, gpointer userdata) +{ + if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) { + ModestWindowPrivate *parent_priv; + ModestWindowMgr *mgr; + gboolean is_fullscreen; + GtkAction *fs_toggle_action; + gboolean active; + + mgr = modest_runtime_get_window_mgr (); + is_fullscreen = (modest_window_mgr_get_fullscreen_mode (mgr))?1:0; + + parent_priv = MODEST_WINDOW_GET_PRIVATE (widget); + + fs_toggle_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewToggleFullscreenMenu"); + active = (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (fs_toggle_action)))?1:0; + if (is_fullscreen != active) { + gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (fs_toggle_action), is_fullscreen); + } + toolbar_resize (MODEST_MSG_VIEW_WINDOW (widget)); + } + + return FALSE; + +} + +static void +modest_msg_view_window_show_toolbar (ModestWindow *self, + gboolean show_toolbar) +{ + ModestMsgViewWindowPrivate *priv = NULL; + ModestWindowPrivate *parent_priv; + GtkWidget *reply_button = NULL, *menu = NULL; + GtkWidget *placeholder = NULL; + gint insert_index; + const gchar *action_name; + GtkAction *action; + + parent_priv = MODEST_WINDOW_GET_PRIVATE(self); + priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self); + + /* Set optimized view status */ + priv->optimized_view = !show_toolbar; + + if (!parent_priv->toolbar) { + parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager, + "/ToolBar"); + gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE); + + priv->progress_toolitem = GTK_WIDGET (gtk_tool_item_new ()); + priv->cancel_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarCancel"); + priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext"); + priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack"); + toolbar_resize (MODEST_MSG_VIEW_WINDOW (self)); + + /* Add ProgressBar (Transfer toolbar) */ + priv->progress_bar = modest_progress_bar_new (); + gtk_widget_set_no_show_all (priv->progress_bar, TRUE); + placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ProgressbarView"); + insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder)); + gtk_container_add (GTK_CONTAINER (priv->progress_toolitem), priv->progress_bar); + gtk_toolbar_insert(GTK_TOOLBAR(parent_priv->toolbar), GTK_TOOL_ITEM (priv->progress_toolitem), insert_index); + + /* Connect cancel 'clicked' signal to abort progress mode */ + g_signal_connect(priv->cancel_toolitem, "clicked", + G_CALLBACK(cancel_progressbar), + self); + + /* Add it to the observers list */ + priv->progress_widgets = g_slist_prepend(priv->progress_widgets, priv->progress_bar); + + /* Add to window */ + hildon_window_add_toolbar (HILDON_WINDOW (self), + GTK_TOOLBAR (parent_priv->toolbar)); + + /* Set reply button tap and hold menu */ + reply_button = gtk_ui_manager_get_widget (parent_priv->ui_manager, + "/ToolBar/ToolbarMessageReply"); + menu = gtk_ui_manager_get_widget (parent_priv->ui_manager, + "/ToolbarReplyCSM"); + gtk_widget_tap_and_hold_setup (GTK_WIDGET (reply_button), menu, NULL, 0); + } + + if (show_toolbar) { + /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */ + /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */ + gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE); + + gtk_widget_show (GTK_WIDGET (parent_priv->toolbar)); + if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self))) + set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_TRANSFER); + else + set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_NORMAL); + + } else { + gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE); + gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar)); + } + + /* Update also the actions (to update the toggles in the + menus), we have to do it manually because some other window + of the same time could have changed it (remember that the + toolbar fullscreen mode is shared by all the windows of the + same type */ + if (modest_window_mgr_get_fullscreen_mode (modest_runtime_get_window_mgr ())) + action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarFullScreenMenu"; + else + action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarNormalScreenMenu"; + + action = gtk_ui_manager_get_action (parent_priv->ui_manager, action_name); + modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), + show_toolbar); +} + +static void +modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard, + GdkEvent *event, + ModestMsgViewWindow *window) +{ + if (!GTK_WIDGET_VISIBLE (window)) + return; + + modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD); +} + +gboolean +modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self) +{ + ModestMsgViewWindowPrivate *priv; + + g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE); + priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self); + + return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER; +} + +static void +cancel_progressbar (GtkToolButton *toolbutton, + ModestMsgViewWindow *self) +{ + GSList *tmp; + ModestMsgViewWindowPrivate *priv; + + priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self); + + /* Get operation observers and cancel its current operation */ + tmp = priv->progress_widgets; + while (tmp) { + modest_progress_object_cancel_current_operation (MODEST_PROGRESS_OBJECT(tmp->data)); + tmp=g_slist_next(tmp); + } +} +static gboolean +observers_empty (ModestMsgViewWindow *self) +{ + GSList *tmp = NULL; + ModestMsgViewWindowPrivate *priv; + gboolean is_empty = TRUE; + guint pending_ops = 0; + + priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self); + tmp = priv->progress_widgets; + + /* Check all observers */ + while (tmp && is_empty) { + pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data)); + is_empty = pending_ops == 0; + + tmp = g_slist_next(tmp); + } + + return is_empty; +} + +static void +on_account_removed (TnyAccountStore *account_store, + TnyAccount *account, + gpointer user_data) +{ + /* Do nothing if it's a transport account, because we only + show the messages of a store account */ + if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) { + const gchar *parent_acc = NULL; + const gchar *our_acc = NULL; + + our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data)); + parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account); + + /* Close this window if I'm showing a message of the removed account */ + if (strcmp (parent_acc, our_acc) == 0) + modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data)); + } +} + +static void +on_mail_operation_started (ModestMailOperation *mail_op, + gpointer user_data) +{ + ModestMsgViewWindow *self; + ModestMailOperationTypeOperation op_type; + GSList *tmp; + ModestMsgViewWindowPrivate *priv; + GObject *source = NULL; + + self = MODEST_MSG_VIEW_WINDOW (user_data); + priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self); + op_type = modest_mail_operation_get_type_operation (mail_op); + tmp = priv->progress_widgets; + source = modest_mail_operation_get_source(mail_op); + if (G_OBJECT (self) == source) { + if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ) { + set_toolbar_transfer_mode(self); + while (tmp) { + modest_progress_object_add_operation ( + MODEST_PROGRESS_OBJECT (tmp->data), + mail_op); + tmp = g_slist_next (tmp); + } + } + } + g_object_unref (source); +} + +static void +on_mail_operation_finished (ModestMailOperation *mail_op, + gpointer user_data) +{ + ModestMsgViewWindow *self; + ModestMailOperationTypeOperation op_type; + GSList *tmp; + ModestMsgViewWindowPrivate *priv; + + self = MODEST_MSG_VIEW_WINDOW (user_data); + priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self); + op_type = modest_mail_operation_get_type_operation (mail_op); + tmp = priv->progress_widgets; + + if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ) { + while (tmp) { + modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data), + mail_op); + tmp = g_slist_next (tmp); + } + + /* If no more operations are being observed, NORMAL mode is enabled again */ + if (observers_empty (self)) { + set_toolbar_mode (self, TOOLBAR_MODE_NORMAL); + } + + /* Update dimming rules. We have to do this right here + and not in view_msg_cb because at that point the + transfer mode is still enabled so the dimming rule + won't let the user delete the message that has been + readed for example */ + modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self)); + modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (self)); + } +} + +static void +on_queue_changed (ModestMailOperationQueue *queue, + ModestMailOperation *mail_op, + ModestMailOperationQueueNotification type, + ModestMsgViewWindow *self) +{ + ModestMsgViewWindowPrivate *priv; + + priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self); + + /* If this operations was created by another window, do nothing */ + if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self))) + return; + + if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) { + priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers, + G_OBJECT (mail_op), + "operation-started", + G_CALLBACK (on_mail_operation_started), + self); + priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers, + G_OBJECT (mail_op), + "operation-finished", + G_CALLBACK (on_mail_operation_finished), + self); + } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) { + priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers, + G_OBJECT (mail_op), + "operation-started"); + priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers, + G_OBJECT (mail_op), + "operation-finished"); + } +} + +TnyList * +modest_msg_view_window_get_attachments (ModestMsgViewWindow *win) +{ + ModestMsgViewWindowPrivate *priv; + TnyList *selected_attachments = NULL; + + g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL); + priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win); + + selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view)); + + return selected_attachments; +} + +typedef struct { + gchar *filepath; + GtkWidget *banner; + guint banner_idle_id; +} DecodeAsyncHelper; + +static gboolean +decode_async_banner_idle (gpointer user_data) +{ + DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data; + + helper->banner_idle_id = 0; + helper->banner = hildon_banner_show_animation (NULL, NULL, _("mail_me_opening")); + g_object_ref (helper->banner); + + return FALSE; +} + +static void +on_decode_to_stream_async_handler (TnyMimePart *mime_part, + gboolean cancelled, + TnyStream *stream, + GError *err, + gpointer user_data) +{ + DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data; + + if (helper->banner_idle_id > 0) { + g_source_remove (helper->banner_idle_id); + helper->banner_idle_id = 0; + } + if (helper->banner) { + gtk_widget_destroy (helper->banner); + } + if (cancelled || err) { + modest_platform_information_banner (NULL, NULL, + _("mail_ib_file_operation_failed")); + goto free; + } + + /* make the file read-only */ + g_chmod(helper->filepath, 0444); + + /* Activate the file */ + modest_platform_activate_file (helper->filepath, tny_mime_part_get_content_type (mime_part)); + + free: + /* Frees */ + g_free (helper->filepath); + g_object_unref (helper->banner); + g_slice_free (DecodeAsyncHelper, helper); +} + +void +modest_msg_view_window_view_attachment (ModestMsgViewWindow *window, + TnyMimePart *mime_part) +{ + ModestMsgViewWindowPrivate *priv; + const gchar *msg_uid; + gchar *attachment_uid = NULL; + gint attachment_index = 0; + TnyList *attachments; + + g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window)); + g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL)); + priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); + + msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window)); + attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view)); + attachment_index = modest_list_index (attachments, (GObject *) mime_part); + g_object_unref (attachments); + + if (msg_uid && attachment_index >= 0) { + attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index); + } + + if (mime_part == NULL) { + gboolean error = FALSE; + TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view)); + if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) { + error = TRUE; + } else if (tny_list_get_length (selected_attachments) > 1) { + hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more")); + error = TRUE; + } else { + TnyIterator *iter; + iter = tny_list_create_iterator (selected_attachments); + mime_part = (TnyMimePart *) tny_iterator_get_current (iter); + g_object_unref (iter); + } + g_object_unref (selected_attachments); + + if (error) + return; + } else { + g_object_ref (mime_part); + } + + if (tny_mime_part_is_purged (mime_part)) { + g_object_unref (mime_part); + return; + } + + if (!modest_tny_mime_part_is_msg (mime_part)) { + gchar *filepath = NULL; + const gchar *att_filename = tny_mime_part_get_filename (mime_part); + gboolean show_error_banner = FALSE; + TnyFsStream *temp_stream = NULL; + temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid, + &filepath); + + if (temp_stream != NULL) { + DecodeAsyncHelper *helper = g_slice_new (DecodeAsyncHelper); + helper->filepath = g_strdup (filepath); + helper->banner = NULL; + helper->banner_idle_id = g_timeout_add (1000, decode_async_banner_idle, helper); + tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream), + on_decode_to_stream_async_handler, + NULL, + helper); + g_object_unref (temp_stream); + /* NOTE: files in the temporary area will be automatically + * cleaned after some time if they are no longer in use */ + } else { + if (filepath) { + const gchar *content_type; + /* the file may already exist but it isn't writable, + * let's try to open it anyway */ + content_type = tny_mime_part_get_content_type (mime_part); + modest_platform_activate_file (filepath, content_type); + } else { + g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__); + show_error_banner = TRUE; + } + } + if (filepath) + g_free (filepath); + if (show_error_banner) + modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed")); + } else { + /* message attachment */ + TnyHeader *header = NULL; + ModestWindowMgr *mgr; + ModestWindow *msg_win = NULL; + gboolean found; + + header = tny_msg_get_header (TNY_MSG (mime_part)); + mgr = modest_runtime_get_window_mgr (); + found = modest_window_mgr_find_registered_header (mgr, header, &msg_win); + + if (found) { + if (msg_win) /* there is already a window for this uid; top it */ + gtk_window_present (GTK_WINDOW(msg_win)); + else + /* if it's found, but there is no msg_win, it's probably in the process of being created; + * thus, we don't do anything */ + g_warning ("window for is already being created"); + } else { + /* it's not found, so create a new window for it */ + modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */ + gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window))); + if (!account) + account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ()); + msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account, attachment_uid); + modest_window_set_zoom (MODEST_WINDOW (msg_win), + modest_window_get_zoom (MODEST_WINDOW (window))); + modest_window_mgr_register_window (mgr, msg_win); + gtk_widget_show_all (GTK_WIDGET (msg_win)); + } + } + g_object_unref (mime_part); +} + +typedef struct +{ + gchar *filename; + TnyMimePart *part; +} SaveMimePartPair; + +typedef struct +{ + GList *pairs; + GtkWidget *banner; + GnomeVFSResult result; +} SaveMimePartInfo; + +static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct); +static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info); +static gpointer save_mime_part_to_file (SaveMimePartInfo *info); +static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info); + +static void +save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct) +{ + + GList *node; + for (node = info->pairs; node != NULL; node = g_list_next (node)) { + SaveMimePartPair *pair = (SaveMimePartPair *) node->data; + g_free (pair->filename); + g_object_unref (pair->part); + g_slice_free (SaveMimePartPair, pair); + } + g_list_free (info->pairs); + info->pairs = NULL; + if (with_struct) { + gtk_widget_destroy (info->banner); + g_slice_free (SaveMimePartInfo, info); + } +} + +static gboolean +idle_save_mime_part_show_result (SaveMimePartInfo *info) +{ + if (info->pairs != NULL) { + save_mime_part_to_file (info); + } else { + /* This is a GDK lock because we are an idle callback and + * hildon_banner_show_information is or does Gtk+ code */ + + gdk_threads_enter (); /* CHECKED */ + save_mime_part_info_free (info, TRUE); + if (info->result == GNOME_VFS_OK) { + hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved")); + } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) { + hildon_banner_show_information (NULL, NULL, dgettext("ke-recv", + "cerm_device_memory_full")); + } else { + hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed")); + } + gdk_threads_leave (); /* CHECKED */ + } + + return FALSE; +} + +static gpointer +save_mime_part_to_file (SaveMimePartInfo *info) +{ + GnomeVFSHandle *handle; + TnyStream *stream; + SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data; + + info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644); + if (info->result == GNOME_VFS_OK) { + GError *error = NULL; + stream = tny_vfs_stream_new (handle); + if (tny_mime_part_decode_to_stream (pair->part, stream, &error) < 0) { + g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error"); + + info->result = GNOME_VFS_ERROR_IO; + } + g_object_unref (G_OBJECT (stream)); + g_object_unref (pair->part); + g_slice_free (SaveMimePartPair, pair); + info->pairs = g_list_delete_link (info->pairs, info->pairs); + } else { + g_warning ("modest: could not create save attachment %s: %s\n", pair->filename, gnome_vfs_result_to_string (info->result)); + save_mime_part_info_free (info, FALSE); + } + + g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info); + return NULL; +} + +static void +save_mime_parts_to_file_with_checks (SaveMimePartInfo *info) +{ + gboolean is_ok = TRUE; + gint replaced_files = 0; + const GList *files = info->pairs; + const GList *iter; + + for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) { + SaveMimePartPair *pair = iter->data; + if (modest_utils_file_exists (pair->filename)) { + replaced_files++; + } + } + if (replaced_files) { + GtkWidget *confirm_overwrite_dialog; + const gchar *message = (replaced_files == 1) ? + _FM("docm_nc_replace_file") : _FM("docm_nc_replace_multiple"); + confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message); + if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) { + is_ok = FALSE; + } + gtk_widget_destroy (confirm_overwrite_dialog); + } + + if (!is_ok) { + save_mime_part_info_free (info, TRUE); + } else { + GtkWidget *banner = hildon_banner_show_animation (NULL, NULL, + _CS("sfil_ib_saving")); + info->banner = banner; + g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL); + } + +} + +static void +save_attachments_response (GtkDialog *dialog, + gint arg1, + gpointer user_data) +{ + TnyList *mime_parts; + gchar *chooser_uri; + GList *files_to_save = NULL; + + mime_parts = TNY_LIST (user_data); + + if (arg1 != GTK_RESPONSE_OK) + goto end; + + chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog)); + + if (!modest_utils_folder_writable (chooser_uri)) { + hildon_banner_show_information + (NULL, NULL, dgettext("hildon-fm", "sfil_ib_readonly_location")); + } else { + TnyIterator *iter; + + iter = tny_list_create_iterator (mime_parts); + while (!tny_iterator_is_done (iter)) { + TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter); + + if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) && + !tny_mime_part_is_purged (mime_part) && + (tny_mime_part_get_filename (mime_part) != NULL)) { + SaveMimePartPair *pair; + + pair = g_slice_new0 (SaveMimePartPair); + + if (tny_list_get_length (mime_parts) > 1) { + gchar *escaped = + gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part)); + pair->filename = g_build_filename (chooser_uri, escaped, NULL); + g_free (escaped); + } else { + pair->filename = g_strdup (chooser_uri); + } + pair->part = mime_part; + files_to_save = g_list_prepend (files_to_save, pair); + } + tny_iterator_next (iter); + } + g_object_unref (iter); + } + g_free (chooser_uri); + + if (files_to_save != NULL) { + SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo); + info->pairs = files_to_save; + info->result = TRUE; + save_mime_parts_to_file_with_checks (info); + } + + end: + /* Free and close the dialog */ + g_object_unref (mime_parts); + gtk_widget_destroy (GTK_WIDGET (dialog)); +} + +void +modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, TnyList *mime_parts) +{ + ModestMsgViewWindowPrivate *priv; + GtkWidget *save_dialog = NULL; + gchar *folder = NULL; + gchar *filename = NULL; + gchar *save_multiple_str = NULL; + + g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window)); + priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); + + if (mime_parts == NULL) { + mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view)); + if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) + return; + } else { + g_object_ref (mime_parts); + } + + /* prepare dialog */ + if (tny_list_get_length (mime_parts) == 1) { + TnyIterator *iter; + /* only one attachment selected */ + iter = tny_list_create_iterator (mime_parts); + TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter); + g_object_unref (iter); + if (!modest_tny_mime_part_is_msg (mime_part) && + modest_tny_mime_part_is_attachment_for_modest (mime_part) && + !tny_mime_part_is_purged (mime_part)) { + filename = g_strdup (tny_mime_part_get_filename (mime_part)); + } else { + /* TODO: show any error? */ + g_warning ("Tried to save a non-file attachment"); + g_object_unref (mime_parts); + return; + } + g_object_unref (mime_part); + } else { + save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"), + tny_list_get_length (mime_parts)); + } + + save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window), + GTK_FILE_CHOOSER_ACTION_SAVE); + + /* set folder */ + folder = g_build_filename (g_get_home_dir (), DEFAULT_FOLDER, NULL); + gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), folder); + g_free (folder); + + /* set filename */ + if (filename) { + gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog), + filename); + g_free (filename); + } + + /* if multiple, set multiple string */ + if (save_multiple_str) { + g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL); + gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files")); + } + + /* We must run this asynchronously, because the hildon dialog + performs a gtk_dialog_run by itself which leads to gdk + deadlocks */ + g_signal_connect (save_dialog, "response", + G_CALLBACK (save_attachments_response), mime_parts); + + gtk_widget_show_all (save_dialog); +} + +static gboolean +show_remove_attachment_information (gpointer userdata) +{ + ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata; + ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); + + /* We're outside the main lock */ + gdk_threads_enter (); + + if (priv->remove_attachment_banner != NULL) { + gtk_widget_destroy (priv->remove_attachment_banner); + g_object_unref (priv->remove_attachment_banner); + } + + priv->remove_attachment_banner = g_object_ref ( + hildon_banner_show_animation (NULL, NULL, _("mcen_ib_removing_attachment"))); + + gdk_threads_leave (); + + return FALSE; +} + +void +modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all) +{ + ModestMsgViewWindowPrivate *priv; + TnyList *mime_parts = NULL; + gchar *confirmation_message; + gint response; + gint n_attachments; + TnyMsg *msg; + TnyIterator *iter; + + g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window)); + priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); + + if (get_all) + mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view)); + else + mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view)); + + /* Remove already purged messages from mime parts list */ + iter = tny_list_create_iterator (mime_parts); + while (!tny_iterator_is_done (iter)) { + TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter)); + tny_iterator_next (iter); + if (tny_mime_part_is_purged (part)) { + tny_list_remove (mime_parts, (GObject *) part); + } + g_object_unref (part); + } + g_object_unref (iter); + + if (tny_list_get_length (mime_parts) == 0) { + g_object_unref (mime_parts); + return; + } + + n_attachments = tny_list_get_length (mime_parts); + if (n_attachments == 1) { + gchar *filename; + TnyMimePart *part; + + iter = tny_list_create_iterator (mime_parts); + part = (TnyMimePart *) tny_iterator_get_current (iter); + g_object_unref (iter); + if (modest_tny_mime_part_is_msg (part)) { + TnyHeader *header; + header = tny_msg_get_header (TNY_MSG (part)); + filename = tny_header_dup_subject (header); + g_object_unref (header); + if (filename == NULL) + filename = g_strdup (_("mail_va_no_subject")); + } else { + filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part))); + } + confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename); + g_free (filename); + g_object_unref (part); + } else { + confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text", + "mcen_nc_purge_files_text", + n_attachments), n_attachments); + } + response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window), + confirmation_message); + g_free (confirmation_message); + + if (response != GTK_RESPONSE_OK) { + g_object_unref (mime_parts); + return; + } + + priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window); + + iter = tny_list_create_iterator (mime_parts); + while (!tny_iterator_is_done (iter)) { + TnyMimePart *part; + + part = (TnyMimePart *) tny_iterator_get_current (iter); + tny_mime_part_set_purged (TNY_MIME_PART (part)); + g_object_unref (part); + tny_iterator_next (iter); + } + g_object_unref (iter); + + msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view)); + tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view)); + tny_msg_rewrite_cache (msg); + tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg); + g_object_unref (msg); + + g_object_unref (mime_parts); + + if (priv->purge_timeout > 0) { + g_source_remove (priv->purge_timeout); + priv->purge_timeout = 0; + } + + if (priv->remove_attachment_banner) { + gtk_widget_destroy (priv->remove_attachment_banner); + g_object_unref (priv->remove_attachment_banner); + priv->remove_attachment_banner = NULL; + } + + +} + + +static void +update_window_title (ModestMsgViewWindow *window) +{ + ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); + TnyMsg *msg = NULL; + TnyHeader *header = NULL; + gchar *subject = NULL; + + msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view)); + + if (msg != NULL) { + header = tny_msg_get_header (msg); + subject = tny_header_dup_subject (header); + g_object_unref (header); + g_object_unref (msg); + } + + if ((subject == NULL)||(subject[0] == '\0')) { + g_free (subject); + subject = g_strdup (_("mail_va_no_subject")); + } + + gtk_window_set_title (GTK_WINDOW (window), subject); +} + + +static void on_move_focus (GtkWidget *widget, + GtkDirectionType direction, + gpointer userdata) +{ + g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus"); +} + +static TnyStream * +fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri) +{ + GnomeVFSResult result; + GnomeVFSHandle *handle = NULL; + GnomeVFSFileInfo *info = NULL; + TnyStream *stream; + + result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ); + if (result != GNOME_VFS_OK) { + *expected_size = 0; + return NULL; + } + + info = gnome_vfs_file_info_new (); + result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT); + if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) { + /* We put a "safe" default size for going to cache */ + *expected_size = (300*1024); + } else { + *expected_size = info->size; + } + gnome_vfs_file_info_unref (info); + + stream = tny_vfs_stream_new (handle); + + return stream; + +} + +typedef struct { + gchar *uri; + gchar *cache_id; + TnyStream *output_stream; + GtkWidget *msg_view; +} FetchImageData; + +gboolean +on_fetch_image_idle_refresh_view (gpointer userdata) +{ + + FetchImageData *fidata = (FetchImageData *) userdata; + g_message ("REFRESH VIEW"); + if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) { + g_message ("QUEUING DRAW"); + gtk_widget_queue_draw (fidata->msg_view); + } + g_object_unref (fidata->msg_view); + g_slice_free (FetchImageData, fidata); + return FALSE; +} + +static gpointer +on_fetch_image_thread (gpointer userdata) +{ + FetchImageData *fidata = (FetchImageData *) userdata; + TnyStreamCache *cache; + TnyStream *cache_stream; + + cache = modest_runtime_get_images_cache (); + cache_stream = tny_stream_cache_get_stream (cache, fidata->cache_id, (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream, (gpointer) fidata->uri); + g_free (fidata->cache_id); + g_free (fidata->uri); + + if (cache_stream != NULL) { + tny_stream_write_to_stream (cache_stream, fidata->output_stream); + tny_stream_close (cache_stream); + g_object_unref (cache_stream); + } + + tny_stream_close (fidata->output_stream); + g_object_unref (fidata->output_stream); + + + gdk_threads_enter (); + g_idle_add (on_fetch_image_idle_refresh_view, fidata); + gdk_threads_leave (); + + return NULL; +} + +static gboolean +on_fetch_image (ModestMsgView *msgview, + const gchar *uri, + TnyStream *stream, + ModestMsgViewWindow *window) +{ + const gchar *current_account; + ModestMsgViewWindowPrivate *priv; + FetchImageData *fidata; + + priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); + + current_account = modest_window_get_active_account (MODEST_WINDOW (window)); + + fidata = g_slice_new0 (FetchImageData); + fidata->msg_view = g_object_ref (msgview); + fidata->uri = g_strdup (uri); + fidata->cache_id = modest_images_cache_get_id (current_account, uri); + fidata->output_stream = g_object_ref (stream); + + if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) { + g_object_unref (fidata->output_stream); + g_free (fidata->cache_id); + g_free (fidata->uri); + g_object_unref (fidata->msg_view); + g_slice_free (FetchImageData, fidata); + tny_stream_close (stream); + return FALSE; + } + + return TRUE;; +} diff --git a/src/hildon2/modest-osso-autosave-callbacks.c b/src/hildon2/modest-osso-autosave-callbacks.c new file mode 100644 index 0000000..e58ef7e --- /dev/null +++ b/src/hildon2/modest-osso-autosave-callbacks.c @@ -0,0 +1,40 @@ +/* Copyright (c) 2007, Nokia Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Nokia Corporation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#include + +void modest_on_osso_application_autosave(gpointer data) +{ + +} + + + + diff --git a/src/hildon2/modest-osso-autosave-callbacks.h b/src/hildon2/modest-osso-autosave-callbacks.h new file mode 100644 index 0000000..3676e8b --- /dev/null +++ b/src/hildon2/modest-osso-autosave-callbacks.h @@ -0,0 +1,40 @@ +/* Copyright (c) 2007, Nokia Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Nokia Corporation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef __MODEST_OSSO_AUTOSAVE_CALLBACKS_H__ +#define __MODEST_OSSO_AUTOSAVE_CALLBACKS_H__ + +#include +#include + +void modest_on_osso_application_autosave(gpointer data); + + +#endif /* __MODEST_OSSO_AUTOSAVE_CALLBACKS_H__ */ diff --git a/src/hildon2/modest-osso-state-saving.c b/src/hildon2/modest-osso-state-saving.c new file mode 100644 index 0000000..03a8f15 --- /dev/null +++ b/src/hildon2/modest-osso-state-saving.c @@ -0,0 +1,51 @@ +/* Copyright (c) 2007, Nokia Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Nokia Corporation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#include +#include "modest-runtime.h" +#include + +void modest_osso_load_state() +{ + /* We don't need to call osso_state_read() + * - we use our general widget-state-saving technique instead. */ +} + +void modest_osso_save_state() +{ + /* We don't need to call osso_state_write() + * - we use our general widget-state-saving technique instead: */ + modest_window_mgr_save_state_for_all_windows( + modest_runtime_get_window_mgr ()); +} + + + + diff --git a/src/hildon2/modest-osso-state-saving.h b/src/hildon2/modest-osso-state-saving.h new file mode 100644 index 0000000..eee23df --- /dev/null +++ b/src/hildon2/modest-osso-state-saving.h @@ -0,0 +1,40 @@ +/* Copyright (c) 2007, Nokia Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Nokia Corporation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef __MODEST_OSSO_STATE_SAVING_H__ +#define __MODEST_OSSO_STATE_SAVING_H__ + +#include + +void modest_osso_load_state(); + +void modest_osso_save_state(); + +#endif /* __MODEST_OSSO_STATE_SAVING_H__ */ diff --git a/src/hildon2/modest-platform.c b/src/hildon2/modest-platform.c new file mode 100644 index 0000000..06ece71 --- /dev/null +++ b/src/hildon2/modest-platform.c @@ -0,0 +1,2118 @@ +/* Copyright (c) 2006, Nokia Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Nokia Corporation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include "maemo/modest-maemo-global-settings-dialog.h" +#include "modest-widget-memory.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "modest-tny-folder.h" +#include "modest-tny-account.h" +#include +#include +#include +#include +#include "modest-hildon-sort-dialog.h" +#include +#include + +#ifdef MODEST_HAVE_MCE +#include +#endif /*MODEST_HAVE_MCE*/ + +#ifdef MODEST_HAVE_ABOOK +#include +#endif /*MODEST_HAVE_ABOOK*/ + +#ifdef MODEST_HAVE_LIBALARM +#include /* For alarm_event_add(), etc. */ +#endif /*MODEST_HAVE_LIBALARM*/ + + +#define HILDON_OSSO_URI_ACTION "uri-action" +#define URI_ACTION_COPY "copy:" +#define MODEST_NEW_MAIL_SOUND_FILE "/usr/share/sounds/ui-new_email.wav" +#define MODEST_NEW_MAIL_LIGHTING_PATTERN "PatternCommunicationEmail" + +static void +on_modest_conf_update_interval_changed (ModestConf* self, + const gchar *key, + ModestConfEvent event, + ModestConfNotificationId id, + gpointer user_data) +{ + g_return_if_fail (key); + + if (strcmp (key, MODEST_CONF_UPDATE_INTERVAL) == 0) { + const guint update_interval_minutes = + modest_conf_get_int (self, MODEST_CONF_UPDATE_INTERVAL, NULL); + modest_platform_set_update_interval (update_interval_minutes); + } +} + + + +static gboolean +check_required_files (void) +{ + FILE *mcc_file = modest_maemo_open_mcc_mapping_file (); + if (!mcc_file) { + g_printerr ("modest: check for mcc file failed\n"); + return FALSE; + } else + fclose (mcc_file); + + if (access(MODEST_PROVIDER_DATA_FILE, R_OK) != 0 && + access(MODEST_MAEMO_PROVIDER_DATA_FILE, R_OK) != 0) { + g_printerr ("modest: cannot find providers data\n"); + return FALSE; + } + + return TRUE; +} + + +/* the gpointer here is the osso_context. */ +gboolean +modest_platform_init (int argc, char *argv[]) +{ + osso_context_t *osso_context; + + osso_hw_state_t hw_state = { 0 }; + DBusConnection *con; + GSList *acc_names; + + if (!check_required_files ()) { + g_printerr ("modest: missing required files\n"); + return FALSE; + } + + osso_context = osso_initialize(PACKAGE,PACKAGE_VERSION, + FALSE, NULL); + if (!osso_context) { + g_printerr ("modest: failed to acquire osso context\n"); + return FALSE; + } + modest_maemo_utils_set_osso_context (osso_context); + + if ((con = osso_get_dbus_connection (osso_context)) == NULL) { + g_printerr ("modest: could not get dbus connection\n"); + return FALSE; + } + + /* Add a D-Bus handler to be used when the main osso-rpc + * D-Bus handler has not handled something. + * We use this for D-Bus methods that need to use more complex types + * than osso-rpc supports. + */ + if (!dbus_connection_add_filter (con, + modest_dbus_req_filter, + NULL, + NULL)) { + + g_printerr ("modest: Could not add D-Bus filter\n"); + return FALSE; + } + + /* Register our simple D-Bus callbacks, via the osso API: */ + osso_return_t result = osso_rpc_set_cb_f(osso_context, + MODEST_DBUS_SERVICE, + MODEST_DBUS_OBJECT, + MODEST_DBUS_IFACE, + modest_dbus_req_handler, NULL /* user_data */); + if (result != OSSO_OK) { + g_printerr ("modest: Error setting D-BUS callback (%d)\n", result); + return FALSE; + } + + /* Register hardware event dbus callback: */ + hw_state.shutdown_ind = TRUE; + osso_hw_set_event_cb(osso_context, NULL, NULL, NULL); + + /* Register osso auto-save callbacks: */ + result = osso_application_set_autosave_cb (osso_context, + modest_on_osso_application_autosave, NULL /* user_data */); + if (result != OSSO_OK) { + g_printerr ("modest: osso_application_set_autosave_cb() failed.\n"); + return FALSE; + } + + + /* Make sure that the update interval is changed whenever its gconf key + * is changed */ + /* CAUTION: we're not using here the + modest_conf_listen_to_namespace because we know that there + are other parts of Modest listening for this namespace, so + we'll receive the notifications anyway. We basically do not + use it because there is no easy way to do the + modest_conf_forget_namespace */ + ModestConf *conf = modest_runtime_get_conf (); + g_signal_connect (G_OBJECT(conf), + "key_changed", + G_CALLBACK (on_modest_conf_update_interval_changed), + NULL); + + /* only force the setting of the default interval, if there are actually + * any accounts */ + acc_names = modest_account_mgr_account_names (modest_runtime_get_account_mgr(), TRUE); + if (acc_names) { + /* Get the initial update interval from gconf: */ + on_modest_conf_update_interval_changed(conf, MODEST_CONF_UPDATE_INTERVAL, + MODEST_CONF_EVENT_KEY_CHANGED, 0, NULL); + modest_account_mgr_free_account_names (acc_names); + } + + +#ifdef MODEST_HAVE_ABOOK + /* initialize the addressbook */ + if (!osso_abook_init (&argc, &argv, osso_context)) { + g_printerr ("modest: failed to initialized addressbook\n"); + return FALSE; + } +#endif /*MODEST_HAVE_ABOOK*/ + + return TRUE; +} + +gboolean +modest_platform_uninit (void) +{ + osso_context_t *osso_context = + modest_maemo_utils_get_osso_context (); + if (osso_context) + osso_deinitialize (osso_context); + + return TRUE; +} + + + + +TnyDevice* +modest_platform_get_new_device (void) +{ + return TNY_DEVICE (tny_maemo_conic_device_new ()); +} + +gchar* +modest_platform_get_file_icon_name (const gchar* name, const gchar* mime_type, + gchar **effective_mime_type) +{ + GString *mime_str = NULL; + gchar *icon_name = NULL; + gchar **icons, **cursor; + + if (!mime_type || g_ascii_strcasecmp (mime_type, "application/octet-stream") == 0) + mime_str = g_string_new (gnome_vfs_get_mime_type_for_name (name)); + else { + mime_str = g_string_new (mime_type); + g_string_ascii_down (mime_str); + } + + icons = hildon_mime_get_icon_names (mime_str->str, NULL); + + for (cursor = icons; cursor; ++cursor) { + if (!g_ascii_strcasecmp (*cursor, "gnome-mime-message") || + !g_ascii_strcasecmp (*cursor, "gnome-mime-message-rfc822")) { + icon_name = g_strdup ("qgn_list_messagin"); + break; + } else if (gtk_icon_theme_has_icon (gtk_icon_theme_get_default(), *cursor)) { + icon_name = g_strdup (*cursor); + break; + } + } + g_strfreev (icons); + + if (effective_mime_type) + *effective_mime_type = g_string_free (mime_str, FALSE); + else + g_string_free (mime_str, TRUE); + + return icon_name; +} + + +static gboolean +checked_hildon_uri_open (const gchar *uri, HildonURIAction *action) +{ + GError *err = NULL; + gboolean result; + + g_return_val_if_fail (uri, FALSE); + + result = hildon_uri_open (uri, action, &err); + if (!result) { + g_printerr ("modest: hildon_uri_open ('%s', %p) failed: %s", + uri, action, err && err->message ? err->message : "unknown error"); + if (err) + g_error_free (err); + } + return result; +} + + + +gboolean +modest_platform_activate_uri (const gchar *uri) +{ + HildonURIAction *action; + gboolean result = FALSE; + GSList *actions, *iter = NULL; + + g_return_val_if_fail (uri, FALSE); + if (!uri) + return FALSE; + + /* don't try to activate file: uri's -- they might confuse the user, + * and/or might have security implications */ + if (!g_str_has_prefix (uri, "file:")) { + + actions = hildon_uri_get_actions_by_uri (uri, -1, NULL); + + for (iter = actions; iter; iter = g_slist_next (iter)) { + action = (HildonURIAction*) iter->data; + if (action && strcmp (hildon_uri_action_get_service (action), + "com.nokia.modest") == 0) { + result = checked_hildon_uri_open (uri, action); + break; + } + } + + /* if we could not open it with email, try something else */ + if (!result) + result = checked_hildon_uri_open (uri, NULL); + } + + if (!result) { + ModestWindow *parent = + modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); + hildon_banner_show_information (parent ? GTK_WIDGET(parent): NULL, NULL, + _("mcen_ib_unsupported_link")); + g_warning ("%s: cannot open uri '%s'", __FUNCTION__,uri); + } + + return result; +} + +gboolean +modest_platform_activate_file (const gchar *path, const gchar *mime_type) +{ + gint result = 0; + DBusConnection *con; + gchar *uri_path = NULL; + + uri_path = gnome_vfs_get_uri_from_local_path (path); + con = osso_get_dbus_connection (modest_maemo_utils_get_osso_context()); + + if (mime_type) + result = hildon_mime_open_file_with_mime_type (con, uri_path, mime_type); + if (result != 1) + result = hildon_mime_open_file (con, uri_path); + if (result != 1) + modest_platform_run_information_dialog (NULL, _("mcen_ni_noregistered_viewer"), FALSE); + + return result != 1; +} + +typedef struct { + GSList *actions; + gchar *uri; +} ModestPlatformPopupInfo; + +static gboolean +delete_uri_popup (GtkWidget *menu, + GdkEvent *event, + gpointer userdata) +{ + ModestPlatformPopupInfo *popup_info = (ModestPlatformPopupInfo *) userdata; + + g_free (popup_info->uri); + hildon_uri_free_actions (popup_info->actions); + + return FALSE; +} + +static void +activate_uri_popup_item (GtkMenuItem *menu_item, + gpointer userdata) +{ + GSList *node; + ModestPlatformPopupInfo *popup_info = (ModestPlatformPopupInfo *) userdata; + const gchar* action_name; + + action_name = g_object_get_data (G_OBJECT(menu_item), HILDON_OSSO_URI_ACTION); + if (!action_name) { + g_printerr ("modest: no action name defined\n"); + return; + } + + /* special handling for the copy menu item -- copy the uri to the clipboard */ + /* if it's a copy thingy, the uri will look like 'copy:http://slashdot.org' */ + if (g_str_has_prefix (action_name, URI_ACTION_COPY)) { + GtkClipboard *clipboard = gtk_clipboard_get (GDK_NONE); + action_name += strlen(URI_ACTION_COPY); /* jump past the prefix */ + + if (g_str_has_prefix (action_name, "mailto:")) /* ignore mailto: prefixes */ + action_name += strlen ("mailto:"); + + gtk_clipboard_set_text (clipboard, action_name, strlen (action_name)); + modest_platform_information_banner (NULL, NULL, _CS("ecoc_ib_edwin_copied")); + return; /* we're done */ + } + + /* now, the real uri-actions... */ + for (node = popup_info->actions; node != NULL; node = g_slist_next (node)) { + HildonURIAction *action = (HildonURIAction *) node->data; + if (strcmp (action_name, hildon_uri_action_get_name (action))==0) { + if (!checked_hildon_uri_open (popup_info->uri, action)) { + ModestWindow *parent = + modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); + hildon_banner_show_information (parent ? GTK_WIDGET(parent): NULL, NULL, + _("mcen_ib_unsupported_link")); + } + break; + } + } +} + +gboolean +modest_platform_show_uri_popup (const gchar *uri) +{ + GSList *actions_list; + + if (uri == NULL) + return FALSE; + + actions_list = hildon_uri_get_actions_by_uri (uri, -1, NULL); + if (actions_list) { + + GtkWidget *menu = gtk_menu_new (); + ModestPlatformPopupInfo *popup_info = g_new0 (ModestPlatformPopupInfo, 1); + + /* don't add actions for file: uri's -- they might confuse the user, + * and/or might have security implications + * we still allow to copy the url though + */ + if (!g_str_has_prefix (uri, "file:")) { + + GSList *node; + popup_info->actions = actions_list; + popup_info->uri = g_strdup (uri); + + for (node = actions_list; node != NULL; node = g_slist_next (node)) { + GtkWidget *menu_item; + const gchar *action_name; + const gchar *translation_domain; + HildonURIAction *action = (HildonURIAction *) node->data; + action_name = hildon_uri_action_get_name (action); + translation_domain = hildon_uri_action_get_translation_domain (action); + menu_item = gtk_menu_item_new_with_label (dgettext(translation_domain, action_name)); + g_object_set_data (G_OBJECT(menu_item), HILDON_OSSO_URI_ACTION, (gpointer)action_name); /* hack */ + g_signal_connect (G_OBJECT (menu_item), "activate", G_CALLBACK (activate_uri_popup_item), + popup_info); + + if (hildon_uri_is_default_action (action, NULL)) { + gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), menu_item); + } else { + gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item); + } + gtk_widget_show (menu_item); + } + } + + /* always add the copy item */ + GtkWidget* menu_item = gtk_menu_item_new_with_label (dgettext("osso-uri", + "uri_link_copy_link_location")); + g_object_set_data_full (G_OBJECT(menu_item), HILDON_OSSO_URI_ACTION, + g_strconcat (URI_ACTION_COPY, uri, NULL), + g_free); + g_signal_connect (G_OBJECT (menu_item), "activate", G_CALLBACK (activate_uri_popup_item),NULL); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item); + gtk_widget_show (menu_item); + + + /* and what to do when the link is deleted */ + g_signal_connect (G_OBJECT (menu), "delete-event", G_CALLBACK (delete_uri_popup), popup_info); + gtk_menu_popup (GTK_MENU(menu), NULL, NULL, NULL, NULL, 1, gtk_get_current_event_time ()); + + } else { + hildon_banner_show_information (NULL, NULL, _("mcen_ib_unsupported_link")); + } + + return TRUE; +} + + +GdkPixbuf* +modest_platform_get_icon (const gchar *name, guint icon_size) +{ + GError *err = NULL; + GdkPixbuf* pixbuf = NULL; + GtkIconTheme *current_theme = NULL; + + g_return_val_if_fail (name, NULL); + + /* strlen == 0 is not really an error; it just + * means the icon is not available + */ + if (!name || strlen(name) == 0) + return NULL; + + current_theme = gtk_icon_theme_get_default (); + pixbuf = gtk_icon_theme_load_icon (current_theme, name, icon_size, + GTK_ICON_LOOKUP_NO_SVG, + &err); + if (!pixbuf) { + g_printerr ("modest: error loading theme icon '%s': %s\n", + name, err->message); + g_error_free (err); + } + return pixbuf; +} + +const gchar* +modest_platform_get_app_name (void) +{ + return _("mcen_ap_name"); +} + +static void +entry_insert_text (GtkEditable *editable, + const gchar *text, + gint length, + gint *position, + gpointer data) +{ + gchar *chars; + gint chars_length; + + chars = gtk_editable_get_chars (editable, 0, -1); + chars_length = g_utf8_strlen (chars, -1); + + /* Show WID-INF036 */ + if (chars_length >= 20) { + hildon_banner_show_information (gtk_widget_get_parent (GTK_WIDGET (data)), NULL, + _CS("ckdg_ib_maximum_characters_reached")); + } else { + if (modest_text_utils_is_forbidden_char (*text, FOLDER_NAME_FORBIDDEN_CHARS)) { + /* Show an error */ + gchar *tmp, *msg; + + tmp = g_strndup (folder_name_forbidden_chars, + FOLDER_NAME_FORBIDDEN_CHARS_LENGTH); + msg = g_strdup_printf (_CS("ckdg_ib_illegal_characters_entered"), tmp); + hildon_banner_show_information (gtk_widget_get_parent (GTK_WIDGET (data)), + NULL, msg); + g_free (msg); + g_free (tmp); + } else { + /* Write the text in the entry if it's valid */ + g_signal_handlers_block_by_func (editable, + (gpointer) entry_insert_text, data); + gtk_editable_insert_text (editable, text, length, position); + g_signal_handlers_unblock_by_func (editable, + (gpointer) entry_insert_text, data); + } + } + /* Do not allow further processing */ + g_signal_stop_emission_by_name (editable, "insert_text"); +} + +static void +entry_changed (GtkEditable *editable, + gpointer user_data) +{ + gchar *chars; + GtkWidget *ok_button; + GList *buttons; + + buttons = gtk_container_get_children (GTK_CONTAINER (GTK_DIALOG (user_data)->action_area)); + ok_button = GTK_WIDGET (buttons->next->data); + + chars = gtk_editable_get_chars (editable, 0, -1); + g_return_if_fail (chars != NULL); + + + if (g_utf8_strlen (chars,-1) >= 21) + hildon_banner_show_information (gtk_widget_get_parent (GTK_WIDGET (user_data)), NULL, + _CS("ckdg_ib_maximum_characters_reached")); + else + gtk_widget_set_sensitive (ok_button, modest_text_utils_validate_folder_name(chars)); + + /* Free */ + g_list_free (buttons); + g_free (chars); +} + + + +static void +on_response (GtkDialog *dialog, + gint response, + gpointer user_data) +{ + GList *child_vbox, *child_hbox; + GtkWidget *hbox, *entry; + TnyFolderStore *parent; + const gchar *new_name; + gboolean exists; + + if (response != GTK_RESPONSE_ACCEPT) + return; + + /* Get entry */ + child_vbox = gtk_container_get_children (GTK_CONTAINER (dialog->vbox)); + hbox = child_vbox->data; + child_hbox = gtk_container_get_children (GTK_CONTAINER (hbox)); + entry = child_hbox->next->data; + + parent = TNY_FOLDER_STORE (user_data); + new_name = gtk_entry_get_text (GTK_ENTRY (entry)); + exists = FALSE; + + /* Look for another folder with the same name */ + if (modest_tny_folder_has_subfolder_with_name (parent, + new_name, + TRUE)) { + exists = TRUE; + } + + if (!exists) { + if (TNY_IS_ACCOUNT (parent) && + modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (parent)) && + modest_tny_local_folders_account_folder_name_in_use (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (parent), + new_name)) { + exists = TRUE; + } + } + + if (exists) { + + /* Show an error */ + hildon_banner_show_information (gtk_widget_get_parent (GTK_WIDGET (dialog)), + NULL, _CS("ckdg_ib_folder_already_exists")); + /* Select the text */ + gtk_entry_select_region (GTK_ENTRY (entry), 0, -1); + gtk_widget_grab_focus (entry); + /* Do not close the dialog */ + g_signal_stop_emission_by_name (dialog, "response"); + } +} + + + +static gint +modest_platform_run_folder_name_dialog (GtkWindow *parent_window, + TnyFolderStore *parent, + const gchar *dialog_title, + const gchar *label_text, + const gchar *suggested_name, + gchar **folder_name) +{ + GtkWidget *accept_btn = NULL; + GtkWidget *dialog, *entry, *label, *hbox; + GList *buttons = NULL; + gint result; + + /* Ask the user for the folder name */ + dialog = gtk_dialog_new_with_buttons (dialog_title, + parent_window, + GTK_DIALOG_MODAL | GTK_DIALOG_NO_SEPARATOR | GTK_DIALOG_DESTROY_WITH_PARENT, + _("mcen_bd_dialog_ok"), + GTK_RESPONSE_ACCEPT, + _("mcen_bd_dialog_cancel"), + GTK_RESPONSE_REJECT, + NULL); + + /* Add accept button (with unsensitive handler) */ + buttons = gtk_container_get_children (GTK_CONTAINER (GTK_DIALOG (dialog)->action_area)); + accept_btn = GTK_WIDGET (buttons->next->data); + /* Create label and entry */ + label = gtk_label_new (label_text); + /* TODO: check that the suggested name does not exist */ + /* We set 21 as maximum because we want to show WID-INF036 + when the user inputs more that 20 */ + entry = gtk_entry_new_with_max_length (21); + if (suggested_name) + gtk_entry_set_text (GTK_ENTRY (entry), suggested_name); + else + gtk_entry_set_text (GTK_ENTRY (entry), _("mcen_ia_default_folder_name")); + gtk_entry_set_width_chars (GTK_ENTRY (entry), + MAX (g_utf8_strlen (gtk_entry_get_text (GTK_ENTRY (entry)), -1), + g_utf8_strlen (_("mcen_ia_default_folder_name"), -1))); + gtk_entry_select_region (GTK_ENTRY (entry), 0, -1); + + /* Connect to the response method to avoid closing the dialog + when an invalid name is selected*/ + g_signal_connect (dialog, + "response", + G_CALLBACK (on_response), + parent); + + /* Track entry changes */ + g_signal_connect (entry, + "insert-text", + G_CALLBACK (entry_insert_text), + dialog); + g_signal_connect (entry, + "changed", + G_CALLBACK (entry_changed), + dialog); + + + /* Some locales like pt_BR need this to get the full window + title shown */ + gtk_widget_set_size_request (GTK_WIDGET (dialog), 300, -1); + + /* Create the hbox */ + hbox = gtk_hbox_new (FALSE, 12); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0); + + /* Add hbox to dialog */ + gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), + hbox, FALSE, FALSE, 0); + modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), + GTK_WINDOW (dialog)); + gtk_widget_show_all (GTK_WIDGET(dialog)); + + result = gtk_dialog_run (GTK_DIALOG(dialog)); + if (result == GTK_RESPONSE_ACCEPT) + *folder_name = g_strdup (gtk_entry_get_text (GTK_ENTRY (entry))); + + gtk_widget_destroy (dialog); + + while (gtk_events_pending ()) + gtk_main_iteration (); + + return result; +} + +gint +modest_platform_run_new_folder_dialog (GtkWindow *parent_window, + TnyFolderStore *parent_folder, + gchar *suggested_name, + gchar **folder_name) +{ + gchar *real_suggested_name = NULL, *tmp = NULL; + gint result; + + if(suggested_name == NULL) + { + const gchar *default_name = _("mcen_ia_default_folder_name"); + unsigned int i; + gchar num_str[3]; + + for(i = 0; i < 100; ++ i) { + gboolean exists = FALSE; + + sprintf(num_str, "%.2u", i); + + if (i == 0) + real_suggested_name = g_strdup (default_name); + else + real_suggested_name = g_strdup_printf (_("mcen_ia_default_folder_name_s"), + num_str); + exists = modest_tny_folder_has_subfolder_with_name (parent_folder, + real_suggested_name, + TRUE); + + if (!exists) + break; + + g_free (real_suggested_name); + } + + /* Didn't find a free number */ + if (i == 100) + real_suggested_name = g_strdup (default_name); + } else { + real_suggested_name = suggested_name; + } + + tmp = g_strconcat (_("mcen_fi_new_folder_name"), ":", NULL); + result = modest_platform_run_folder_name_dialog (parent_window, + parent_folder, + _("mcen_ti_new_folder"), + tmp, + real_suggested_name, + folder_name); + g_free (tmp); + + if (suggested_name == NULL) + g_free(real_suggested_name); + + return result; +} + +gint +modest_platform_run_rename_folder_dialog (GtkWindow *parent_window, + TnyFolderStore *parent_folder, + const gchar *suggested_name, + gchar **folder_name) +{ + g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent_folder), GTK_RESPONSE_REJECT); + + return modest_platform_run_folder_name_dialog (parent_window, + parent_folder, + _HL("ckdg_ti_rename_folder"), + _HL("ckdg_fi_rename_name"), + suggested_name, + folder_name); +} + + + +static void +on_destroy_dialog (GtkWidget *dialog) +{ + /* This could happen when the dialogs get programatically + hidden or destroyed (for example when closing the + application while a dialog is being shown) */ + if (!GTK_IS_WIDGET (dialog)) + return; + + gtk_widget_destroy (dialog); + + if (gtk_events_pending ()) + gtk_main_iteration (); +} + +gint +modest_platform_run_confirmation_dialog (GtkWindow *parent_window, + const gchar *message) +{ + GtkWidget *dialog; + gint response; + + dialog = hildon_note_new_confirmation (parent_window, message); + modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), + GTK_WINDOW (dialog)); + + response = gtk_dialog_run (GTK_DIALOG (dialog)); + + on_destroy_dialog (dialog); + + return response; +} + +gint +modest_platform_run_confirmation_dialog_with_buttons (GtkWindow *parent_window, + const gchar *message, + const gchar *button_accept, + const gchar *button_cancel) +{ + GtkWidget *dialog; + gint response; + + dialog = hildon_note_new_confirmation_add_buttons (parent_window, message, + button_accept, GTK_RESPONSE_ACCEPT, + button_cancel, GTK_RESPONSE_CANCEL, + NULL); + modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), + GTK_WINDOW (dialog)); + + response = gtk_dialog_run (GTK_DIALOG (dialog)); + + on_destroy_dialog (dialog); + + return response; +} + +gint +modest_platform_run_yes_no_dialog (GtkWindow *parent_window, + const gchar *message) +{ + GtkWidget *dialog; + gint response; + + dialog = hildon_note_new_confirmation_add_buttons (parent_window, message, + _("mcen_bd_yes"), GTK_RESPONSE_YES, + _("mcen_bd_no"), GTK_RESPONSE_NO, + NULL); + modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), GTK_WINDOW (dialog)); + response = gtk_dialog_run (GTK_DIALOG (dialog)); + + on_destroy_dialog (dialog); + + return response; +} + + + +void +modest_platform_run_information_dialog (GtkWindow *parent_window, + const gchar *message, + gboolean block) +{ + GtkWidget *note; + + note = hildon_note_new_information (parent_window, message); + if (block) + modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), + GTK_WINDOW (note)); + + if (block) { + gtk_dialog_run (GTK_DIALOG (note)); + + on_destroy_dialog (note); + } else { + g_signal_connect_swapped (note, + "response", + G_CALLBACK (on_destroy_dialog), + note); + + gtk_widget_show_all (note); + } +} + +typedef struct _ConnectAndWaitData { + GMutex *mutex; + GMainLoop *wait_loop; + gboolean has_callback; + gulong handler; +} ConnectAndWaitData; + + +static void +quit_wait_loop (TnyAccount *account, + ConnectAndWaitData *data) +{ + /* Set the has_callback to TRUE (means that the callback was + executed and wake up every code waiting for cond to be + TRUE */ + g_mutex_lock (data->mutex); + data->has_callback = TRUE; + if (data->wait_loop) + g_main_loop_quit (data->wait_loop); + g_mutex_unlock (data->mutex); +} + +static void +on_connection_status_changed (TnyAccount *account, + TnyConnectionStatus status, + gpointer user_data) +{ + TnyConnectionStatus conn_status; + ConnectAndWaitData *data; + + /* Ignore if reconnecting or disconnected */ + conn_status = tny_account_get_connection_status (account); + if (conn_status == TNY_CONNECTION_STATUS_RECONNECTING || + conn_status == TNY_CONNECTION_STATUS_DISCONNECTED) + return; + + /* Remove the handler */ + data = (ConnectAndWaitData *) user_data; + g_signal_handler_disconnect (account, data->handler); + + /* Quit from wait loop */ + quit_wait_loop (account, (ConnectAndWaitData *) user_data); +} + +static void +on_tny_camel_account_set_online_cb (TnyCamelAccount *account, + gboolean canceled, + GError *err, + gpointer user_data) +{ + /* Quit from wait loop */ + quit_wait_loop (TNY_ACCOUNT (account), (ConnectAndWaitData *) user_data); +} + +gboolean +modest_platform_connect_and_wait (GtkWindow *parent_window, + TnyAccount *account) +{ + ConnectAndWaitData *data = NULL; + gboolean device_online; + TnyDevice *device; + TnyConnectionStatus conn_status; + gboolean user_requested; + + device = modest_runtime_get_device(); + device_online = tny_device_is_online (device); + + /* Whether the connection is user requested or automatically + requested, for example via D-Bus */ + user_requested = (parent_window) ? TRUE : FALSE; + + /* If there is no account check only the device status */ + if (!account) { + if (device_online) + return TRUE; + else + return tny_maemo_conic_device_connect (TNY_MAEMO_CONIC_DEVICE (device), + NULL, user_requested); + } + + /* Return if the account is already connected */ + conn_status = tny_account_get_connection_status (account); + if (device_online && conn_status == TNY_CONNECTION_STATUS_CONNECTED) + return TRUE; + + /* Create the helper */ + data = g_slice_new0 (ConnectAndWaitData); + data->mutex = g_mutex_new (); + data->has_callback = FALSE; + + /* Connect the device */ + if (!device_online) { + /* Track account connection status changes */ + data->handler = g_signal_connect (account, "connection-status-changed", + G_CALLBACK (on_connection_status_changed), + data); + /* Try to connect the device */ + device_online = tny_maemo_conic_device_connect (TNY_MAEMO_CONIC_DEVICE (device), + NULL, user_requested); + + /* If the device connection failed then exit */ + if (!device_online && data->handler) + goto frees; + } else { + /* Force a reconnection of the account */ + tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (account), TRUE, + on_tny_camel_account_set_online_cb, data); + } + + /* Wait until the callback is executed */ + g_mutex_lock (data->mutex); + if (!data->has_callback) { + data->wait_loop = g_main_loop_new (g_main_context_new (), FALSE); + gdk_threads_leave (); + g_mutex_unlock (data->mutex); + g_main_loop_run (data->wait_loop); + g_mutex_lock (data->mutex); + gdk_threads_enter (); + } + g_mutex_unlock (data->mutex); + + frees: + if (data) { + if (g_signal_handler_is_connected (account, data->handler)) + g_signal_handler_disconnect (account, data->handler); + g_mutex_free (data->mutex); + g_main_loop_unref (data->wait_loop); + g_slice_free (ConnectAndWaitData, data); + } + + conn_status = tny_account_get_connection_status (account); + return (conn_status == TNY_CONNECTION_STATUS_CONNECTED) ? TRUE: FALSE; +} + +gboolean +modest_platform_connect_and_wait_if_network_account (GtkWindow *parent_window, TnyAccount *account) +{ + if (tny_account_get_account_type (account) == TNY_ACCOUNT_TYPE_STORE) { + if (!TNY_IS_CAMEL_POP_STORE_ACCOUNT (account) && + !TNY_IS_CAMEL_IMAP_STORE_ACCOUNT (account)) { + /* This must be a maildir account, which does not require a connection: */ + return TRUE; + } + } + + return modest_platform_connect_and_wait (parent_window, account); +} + +gboolean +modest_platform_connect_and_wait_if_network_folderstore (GtkWindow *parent_window, TnyFolderStore *folder_store) +{ + if (!folder_store) + return TRUE; /* Maybe it is something local. */ + + gboolean result = TRUE; + if (TNY_IS_FOLDER (folder_store)) { + /* Get the folder's parent account: */ + TnyAccount *account = tny_folder_get_account(TNY_FOLDER (folder_store)); + if (account != NULL) { + result = modest_platform_connect_and_wait_if_network_account (NULL, account); + g_object_unref (account); + } + } else if (TNY_IS_ACCOUNT (folder_store)) { + /* Use the folder store as an account: */ + result = modest_platform_connect_and_wait_if_network_account (NULL, TNY_ACCOUNT (folder_store)); + } + + return result; +} + +GtkWidget * +modest_platform_create_sort_dialog (GtkWindow *parent_window) +{ + GtkWidget *dialog; + + dialog = modest_hildon_sort_dialog_new (parent_window); + + hildon_help_dialog_help_enable (GTK_DIALOG(dialog), + "applications_email_sort", + modest_maemo_utils_get_osso_context()); + + return dialog; +} + + +gboolean +modest_platform_set_update_interval (guint minutes) +{ +#ifdef MODEST_HAVE_LIBALARM + + ModestConf *conf = modest_runtime_get_conf (); + if (!conf) + return FALSE; + + cookie_t alarm_cookie = modest_conf_get_int (conf, MODEST_CONF_ALARM_ID, NULL); + + /* Delete any existing alarm, + * because we will replace it: */ + if (alarm_cookie) { + if (alarmd_event_del(alarm_cookie) != 1) + g_warning ("%s: alarm %d was not on the queue", __FUNCTION__, (int)alarm_cookie); + alarm_cookie = 0; + modest_conf_set_int (conf, MODEST_CONF_ALARM_ID, 0, NULL); + } + + /* 0 means no updates: */ + if (minutes == 0) + return TRUE; + + + /* Register alarm: */ + + /* Set the interval in alarm_event_t structure: */ + alarm_event_t *event = alarm_event_create (); + alarm_event_add_actions (event, 1); + alarm_action_t *action = alarm_event_get_action (event, 0); + event->alarm_time = minutes * 60; /* seconds */ + + /* Set recurrence every few minutes: */ + event->recur_secs = minutes*60; + event->recur_count = -1; /* Means infinite */ + + /* Specify what should happen when the alarm happens: + * It should call this D-Bus method: */ + + action->dbus_path = g_strdup(MODEST_DBUS_OBJECT); + action->dbus_interface = g_strdup (MODEST_DBUS_IFACE); + action->dbus_service = g_strdup (MODEST_DBUS_SERVICE); + action->dbus_name = g_strdup (MODEST_DBUS_METHOD_SEND_RECEIVE); + action->flags = ALARM_ACTION_TYPE_DBUS | ALARM_ACTION_DBUS_USE_ACTIVATION; + + /* Use ALARM_EVENT_NO_DIALOG: Otherwise, a dialog will be shown if + * exec_name or dbus_path is NULL, even though we have specified no dialog text. + * Also use ALARM_EVENT_ACTIVATION so that modest is started (without UI) to get emails + * This is why we want to use the Alarm API instead of just g_timeout_add(). + * (The old maemo email-client did this, though it isn't specified in the UI spec.) + * ALARM_EVENT_CONNECTED will prevent the alarm from being called in case that the device is offline + */ + event->flags = ALARM_EVENT_CONNECTED; + + alarm_cookie = alarmd_event_add (event); + + /* now, free it */ + alarm_event_delete (event); + + /* Store the alarm ID in GConf, so we can remove it later: + * This is apparently valid between application instances. */ + modest_conf_set_int (conf, MODEST_CONF_ALARM_ID, alarm_cookie, NULL); + + if (!alarm_cookie) { + /* Error */ + g_debug ("Error setting alarm event. \n"); + + return FALSE; + } +#endif /* MODEST_HAVE_LIBALARM */ + return TRUE; +} + +void +modest_platform_push_email_notification(void) +{ + gboolean play_sound; + ModestWindow *main_window; + gboolean screen_on = TRUE, app_in_foreground; + + /* Check whether or not we should play a sound */ + play_sound = modest_conf_get_bool (modest_runtime_get_conf (), + MODEST_CONF_PLAY_SOUND_MSG_ARRIVE, + NULL); + + /* Get the screen status */ + main_window = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr (), FALSE); + if (main_window) + screen_on = modest_main_window_screen_is_on (MODEST_MAIN_WINDOW (main_window)); + + /* Get the window status */ + app_in_foreground = hildon_program_get_is_topmost (hildon_program_get_instance ()); + + /* If the screen is on and the app is in the + foreground we don't show anything */ + if (!(screen_on && app_in_foreground)) { + /* Play a sound */ + if (play_sound) + hildon_play_system_sound (MODEST_NEW_MAIL_SOUND_FILE); + + /* Activate LED. This must be deactivated by + modest_platform_remove_new_mail_notifications */ +#ifdef MODEST_HAVE_MCE + osso_rpc_run_system (modest_maemo_utils_get_osso_context (), + MCE_SERVICE, + MCE_REQUEST_PATH, + MCE_REQUEST_IF, + MCE_ACTIVATE_LED_PATTERN, + NULL, + DBUS_TYPE_STRING, MODEST_NEW_MAIL_LIGHTING_PATTERN, + DBUS_TYPE_INVALID); +#endif + } +} + +void +modest_platform_on_new_headers_received (TnyList *header_list, + gboolean show_visual) +{ + g_return_if_fail (TNY_IS_LIST(header_list)); + + if (tny_list_get_length(header_list) == 0) { + g_warning ("%s: header list is empty", __FUNCTION__); + return; + } + + if (!show_visual) { + modest_platform_push_email_notification (); + /* We do a return here to avoid indentation with an else */ + return; + } + +#ifdef MODEST_HAVE_HILDON_NOTIFY + gboolean play_sound; + + /* Check whether or not we should play a sound */ + play_sound = modest_conf_get_bool (modest_runtime_get_conf (), + MODEST_CONF_PLAY_SOUND_MSG_ARRIVE, + NULL); + + HildonNotification *notification; + TnyIterator *iter; + GSList *notifications_list = NULL; + + /* Get previous notifications ids */ + notifications_list = modest_conf_get_list (modest_runtime_get_conf (), + MODEST_CONF_NOTIFICATION_IDS, + MODEST_CONF_VALUE_INT, NULL); + + iter = tny_list_create_iterator (header_list); + while (!tny_iterator_is_done (iter)) { + gchar *url = NULL, *display_address = NULL, *summary = NULL; + const gchar *display_date; + TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter)); + TnyFolder *folder = tny_header_get_folder (header); + gboolean first_notification = TRUE; + gint notif_id; + gchar *str; + + /* constant string, don't free */ + display_date = modest_text_utils_get_display_date (tny_header_get_date_received (header)); + + display_address = tny_header_dup_from (header); + modest_text_utils_get_display_address (display_address); /* string is changed in-place */ + + summary = g_strdup_printf ("%s - %s", display_date, display_address); + str = tny_header_dup_subject (header); + notification = hildon_notification_new (summary, + str, + "qgn_list_messagin", + "email.arrive"); + g_free (str); + /* Create the message URL */ + str = tny_header_dup_uid (header); + url = g_strdup_printf ("%s/%s", tny_folder_get_url_string (folder), + str); + g_free (str); + + hildon_notification_add_dbus_action(notification, + "default", + "Cancel", + MODEST_DBUS_SERVICE, + MODEST_DBUS_OBJECT, + MODEST_DBUS_IFACE, + MODEST_DBUS_METHOD_OPEN_MESSAGE, + G_TYPE_STRING, url, + -1); + + /* Play sound if the user wants. Show the LED + pattern. Show and play just one */ + if (G_UNLIKELY (first_notification)) { + first_notification = FALSE; + if (play_sound) { + notify_notification_set_hint_string(NOTIFY_NOTIFICATION (notification), + "sound-file", MODEST_NEW_MAIL_SOUND_FILE); + } + + /* Set the led pattern */ + notify_notification_set_hint_int32 (NOTIFY_NOTIFICATION (notification), + "dialog-type", 4); + notify_notification_set_hint_string(NOTIFY_NOTIFICATION (notification), + "led-pattern", + MODEST_NEW_MAIL_LIGHTING_PATTERN); + } + + /* Notify. We need to do this in an idle because this function + could be called from a thread */ + notify_notification_show (NOTIFY_NOTIFICATION (notification), NULL); + + /* Save id in the list */ + g_object_get(G_OBJECT(notification), "id", ¬if_id, NULL); + notifications_list = g_slist_prepend (notifications_list, GINT_TO_POINTER(notif_id)); + /* We don't listen for the "closed" signal, because we + don't care about if the notification was removed or + not to store the list in gconf */ + + /* Free & carry on */ + g_free (display_address); + g_free (summary); + g_free (url); + g_object_unref (folder); + g_object_unref (header); + tny_iterator_next (iter); + } + g_object_unref (iter); + + /* Save the ids */ + modest_conf_set_list (modest_runtime_get_conf (), MODEST_CONF_NOTIFICATION_IDS, + notifications_list, MODEST_CONF_VALUE_INT, NULL); + + g_slist_free (notifications_list); + +#endif /*MODEST_HAVE_HILDON_NOTIFY*/ +} + +void +modest_platform_remove_new_mail_notifications (gboolean only_visuals) +{ + if (only_visuals) { +#ifdef MODEST_HAVE_MCE + osso_rpc_run_system (modest_maemo_utils_get_osso_context (), + MCE_SERVICE, + MCE_REQUEST_PATH, + MCE_REQUEST_IF, + MCE_DEACTIVATE_LED_PATTERN, + NULL, + DBUS_TYPE_STRING, MODEST_NEW_MAIL_LIGHTING_PATTERN, + DBUS_TYPE_INVALID); +#endif + return; + } + +#ifdef MODEST_HAVE_HILDON_NOTIFY + GSList *notif_list = NULL; + + /* Get previous notifications ids */ + notif_list = modest_conf_get_list (modest_runtime_get_conf (), + MODEST_CONF_NOTIFICATION_IDS, + MODEST_CONF_VALUE_INT, NULL); + + while (notif_list) { + gint notif_id; + NotifyNotification *notif; + + /* Nasty HACK to remove the notifications, set the id + of the existing ones and then close them */ + notif_id = GPOINTER_TO_INT(notif_list->data); + notif = notify_notification_new("dummy", NULL, NULL, NULL); + g_object_set(G_OBJECT(notif), "id", notif_id, NULL); + + /* Close the notification, note that some ids could be + already invalid, but we don't care because it does + not fail */ + notify_notification_close(notif, NULL); + g_object_unref(notif); + + /* Delete the link, it's like going to the next */ + notif_list = g_slist_delete_link (notif_list, notif_list); + } + + /* Save the ids */ + modest_conf_set_list (modest_runtime_get_conf (), MODEST_CONF_NOTIFICATION_IDS, + notif_list, MODEST_CONF_VALUE_INT, NULL); + + g_slist_free (notif_list); + +#endif /* MODEST_HAVE_HILDON_NOTIFY */ +} + + + +GtkWidget * +modest_platform_get_global_settings_dialog () +{ + return modest_maemo_global_settings_dialog_new (); +} + +void +modest_platform_show_help (GtkWindow *parent_window, + const gchar *help_id) +{ + osso_return_t result; + g_return_if_fail (help_id); + + result = hildon_help_show (modest_maemo_utils_get_osso_context(), + help_id, HILDON_HELP_SHOW_DIALOG); + + if (result != OSSO_OK) { + gchar *error_msg; + error_msg = g_strdup_printf ("FIXME The help topic %s could not be found", help_id); + hildon_banner_show_information (GTK_WIDGET (parent_window), + NULL, + error_msg); + g_free (error_msg); + } +} + +void +modest_platform_show_search_messages (GtkWindow *parent_window) +{ + osso_return_t result = OSSO_ERROR; + + result = osso_rpc_run_with_defaults (modest_maemo_utils_get_osso_context(), + "osso_global_search", + "search_email", NULL, DBUS_TYPE_INVALID); + + if (result != OSSO_OK) { + g_warning ("%s: osso_rpc_run_with_defaults() failed.\n", __FUNCTION__); + } +} + +void +modest_platform_show_addressbook (GtkWindow *parent_window) +{ + osso_return_t result = OSSO_ERROR; + + result = osso_rpc_run_with_defaults (modest_maemo_utils_get_osso_context(), + "osso_addressbook", + "top_application", NULL, DBUS_TYPE_INVALID); + + if (result != OSSO_OK) { + g_warning ("%s: osso_rpc_run_with_defaults() failed.\n", __FUNCTION__); + } +} + +GtkWidget * +modest_platform_create_folder_view (TnyFolderStoreQuery *query) +{ + GtkWidget *widget = modest_folder_view_new (query); + + /* Show one account by default */ + modest_folder_view_set_style (MODEST_FOLDER_VIEW (widget), + MODEST_FOLDER_VIEW_STYLE_SHOW_ONE); + + /* Restore settings */ + modest_widget_memory_restore (modest_runtime_get_conf(), + G_OBJECT (widget), + MODEST_CONF_FOLDER_VIEW_KEY); + + return widget; +} + +void +banner_finish (gpointer data, GObject *object) +{ + ModestWindowMgr *mgr = (ModestWindowMgr *) data; + modest_window_mgr_unregister_banner (mgr); + g_object_unref (mgr); +} + +void +modest_platform_information_banner (GtkWidget *parent, + const gchar *icon_name, + const gchar *text) +{ + GtkWidget *banner, *banner_parent = NULL; + ModestWindowMgr *mgr = modest_runtime_get_window_mgr (); + + if (modest_window_mgr_num_windows (mgr) == 0) + return; + + if (parent && GTK_IS_WINDOW (parent)) { + /* If the window is the active one then show the + banner on top of this window */ + if (gtk_window_is_active (GTK_WINDOW (parent))) + banner_parent = parent; + /* If the window is not the topmost but it's visible + (it's minimized for example) then show the banner + with no parent */ + else if (GTK_WIDGET_VISIBLE (parent)) + banner_parent = NULL; + /* If the window is hidden (like the main window when + running in the background) then do not show + anything */ + else + return; + } + + + banner = hildon_banner_show_information (banner_parent, icon_name, text); + + modest_window_mgr_register_banner (mgr); + g_object_ref (mgr); + g_object_weak_ref ((GObject *) banner, banner_finish, mgr); +} + +void +modest_platform_information_banner_with_timeout (GtkWidget *parent, + const gchar *icon_name, + const gchar *text, + gint timeout) +{ + GtkWidget *banner; + + if (modest_window_mgr_num_windows (modest_runtime_get_window_mgr ()) == 0) + return; + + banner = hildon_banner_show_information (parent, icon_name, text); + hildon_banner_set_timeout(HILDON_BANNER(banner), timeout); +} + +GtkWidget * +modest_platform_animation_banner (GtkWidget *parent, + const gchar *animation_name, + const gchar *text) +{ + GtkWidget *inf_note = NULL; + + g_return_val_if_fail (text != NULL, NULL); + + if (modest_window_mgr_num_windows (modest_runtime_get_window_mgr ()) == 0) + return NULL; + + /* If the parent is not visible then do not show */ + if (parent && !GTK_WIDGET_VISIBLE (parent)) + return NULL; + + inf_note = hildon_banner_show_animation (parent, animation_name, text); + + return inf_note; +} + +typedef struct +{ + GMainLoop* loop; + TnyAccount *account; + gboolean is_online; + gint count_tries; +} CheckAccountIdleData; + +#define NUMBER_OF_TRIES 10 /* Try approx every second, ten times. */ + +static gboolean +on_timeout_check_account_is_online(CheckAccountIdleData* data) +{ + gboolean stop_trying = FALSE; + g_return_val_if_fail (data && data->account, FALSE); + + printf ("DEBUG: %s: tny_account_get_connection_status()==%d\n", __FUNCTION__, + tny_account_get_connection_status (data->account)); + + if (data && data->account && + /* We want to wait until TNY_CONNECTION_STATUS_INIT has changed to something else, + * after which the account is likely to be usable, or never likely to be usable soon: */ + (tny_account_get_connection_status (data->account) != TNY_CONNECTION_STATUS_INIT) ) + { + data->is_online = TRUE; + + stop_trying = TRUE; + } else { + /* Give up if we have tried too many times: */ + if (data->count_tries >= NUMBER_OF_TRIES) { + stop_trying = TRUE; + } else { + /* Wait for another timeout: */ + ++(data->count_tries); + } + } + + if (stop_trying) { + /* Allow the function that requested this idle callback to continue: */ + if (data->loop) + g_main_loop_quit (data->loop); + + if (data->account) + g_object_unref (data->account); + + return FALSE; /* Don't call this again. */ + } else { + return TRUE; /* Call this timeout callback again. */ + } +} + +/* Return TRUE immediately if the account is already online, + * otherwise check every second for NUMBER_OF_TRIES seconds and return TRUE as + * soon as the account is online, or FALSE if the account does + * not become online in the NUMBER_OF_TRIES seconds. + * This is useful when the D-Bus method was run immediately after + * the application was started (when using D-Bus activation), + * because the account usually takes a short time to go online. + * The return value is maybe not very useful. + */ +gboolean +modest_platform_check_and_wait_for_account_is_online(TnyAccount *account) +{ + g_return_val_if_fail (account, FALSE); + + printf ("DEBUG: %s: account id=%s\n", __FUNCTION__, tny_account_get_id (account)); + + if (!tny_device_is_online (modest_runtime_get_device())) { + printf ("DEBUG: %s: device is offline.\n", __FUNCTION__); + return FALSE; + } + + /* The local_folders account never seems to leave TNY_CONNECTION_STATUS_INIT, + * so we avoid wait unnecessarily: */ + if (!TNY_IS_CAMEL_POP_STORE_ACCOUNT (account) && + !TNY_IS_CAMEL_IMAP_STORE_ACCOUNT (account) ) { + return TRUE; + } + + printf ("DEBUG: %s: tny_account_get_connection_status()==%d\n", + __FUNCTION__, tny_account_get_connection_status (account)); + + /* The POP & IMAP store accounts seem to be TNY_CONNECTION_STATUS_DISCONNECTED, + * and that seems to be an OK time to use them. Maybe it's just TNY_CONNECTION_STATUS_INIT that + * we want to avoid. */ + if (tny_account_get_connection_status (account) != TNY_CONNECTION_STATUS_INIT) + return TRUE; + + /* This blocks on the result: */ + CheckAccountIdleData *data = g_slice_new0 (CheckAccountIdleData); + data->is_online = FALSE; + data->account = account; + g_object_ref (data->account); + data->count_tries = 0; + + GMainContext *context = NULL; /* g_main_context_new (); */ + data->loop = g_main_loop_new (context, FALSE /* not running */); + + g_timeout_add (1000, (GSourceFunc)(on_timeout_check_account_is_online), data); + + /* This main loop will run until the idle handler has stopped it: */ + g_main_loop_run (data->loop); + + g_main_loop_unref (data->loop); + /* g_main_context_unref (context); */ + + g_slice_free (CheckAccountIdleData, data); + + return data->is_online; +} + + + +static void +on_cert_dialog_response (GtkDialog *dialog, gint response_id, const gchar* cert) +{ + /* GTK_RESPONSE_HELP means we need to show the certificate */ + if (response_id == GTK_RESPONSE_APPLY) { + GtkWidget *note; + gchar *msg; + + /* Do not close the dialog */ + g_signal_stop_emission_by_name (dialog, "response"); + + msg = g_strdup_printf (_("mcen_ni_view_unknown_certificate"), cert); + note = hildon_note_new_information (GTK_WINDOW(dialog), msg); + gtk_dialog_run (GTK_DIALOG(note)); + gtk_widget_destroy (note); + } +} + + +gboolean +modest_platform_run_certificate_confirmation_dialog (const gchar* server_name, + const gchar *certificate) +{ + GtkWidget *note; + gint response; + ModestWindow *main_win; + + if (!modest_window_mgr_main_window_exists (modest_runtime_get_window_mgr())) { + g_warning ("%s: don't show dialogs if there's no main window; assuming 'Cancel'", + __FUNCTION__); + return FALSE; + } + + /* don't create it */ + main_win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); + g_return_val_if_fail (main_win, FALSE); /* should not happen */ + + + gchar *question = g_strdup_printf (_("mcen_nc_unknown_certificate"), + server_name); + + /* We use GTK_RESPONSE_APPLY because we want the button in the + middle of OK and CANCEL the same as the browser does for + example. With GTK_RESPONSE_HELP the view button is aligned + to the left while the other two to the right */ + note = hildon_note_new_confirmation_add_buttons ( + GTK_WINDOW(main_win), + question, + _("mcen_bd_dialog_ok"), GTK_RESPONSE_OK, + _("mcen_bd_view"), GTK_RESPONSE_APPLY, /* abusing this... */ + _("mcen_bd_dialog_cancel"), GTK_RESPONSE_CANCEL, + NULL, NULL); + + g_signal_connect (G_OBJECT(note), "response", + G_CALLBACK(on_cert_dialog_response), + (gpointer) certificate); + + modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), + GTK_WINDOW (note)); + response = gtk_dialog_run(GTK_DIALOG(note)); + + on_destroy_dialog (note); + g_free (question); + + return response == GTK_RESPONSE_OK; +} + +gboolean +modest_platform_run_alert_dialog (const gchar* prompt, + gboolean is_question) +{ + ModestWindow *main_win; + + if (!modest_window_mgr_main_window_exists (modest_runtime_get_window_mgr())) { + g_warning ("%s:\n'%s'\ndon't show dialogs if there's no main window;" + " assuming 'Cancel' for questions, 'Ok' otherwise", prompt, __FUNCTION__); + return is_question ? FALSE : TRUE; + } + + main_win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr (), FALSE); + g_return_val_if_fail (main_win, FALSE); /* should not happen */ + + gboolean retval = TRUE; + if (is_question) { + /* The Tinymail documentation says that we should show Yes and No buttons, + * when it is a question. + * Obviously, we need tinymail to use more specific error codes instead, + * so we know what buttons to show. */ + GtkWidget *dialog = GTK_WIDGET (hildon_note_new_confirmation (GTK_WINDOW (main_win), + prompt)); + modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), + GTK_WINDOW (dialog)); + + const int response = gtk_dialog_run (GTK_DIALOG (dialog)); + retval = (response == GTK_RESPONSE_YES) || (response == GTK_RESPONSE_OK); + + on_destroy_dialog (dialog); + } else { + /* Just show the error text and use the default response: */ + modest_platform_run_information_dialog (GTK_WINDOW (main_win), + prompt, FALSE); + } + return retval; +} + +/***************/ +typedef struct { + GtkWindow *parent_window; + ModestConnectedPerformer callback; + TnyAccount *account; + gpointer user_data; + gchar *iap; + TnyDevice *device; +} OnWentOnlineInfo; + +static void +on_went_online_info_free (OnWentOnlineInfo *info) +{ + /* And if we cleanup, we DO cleanup :-) */ + + if (info->device) + g_object_unref (info->device); + if (info->iap) + g_free (info->iap); + if (info->parent_window) + g_object_unref (info->parent_window); + if (info->account) + g_object_unref (info->account); + + g_slice_free (OnWentOnlineInfo, info); + + /* We're done ... */ + + return; +} + +static void +on_account_went_online (TnyCamelAccount *account, gboolean canceled, GError *err, gpointer user_data) +{ + OnWentOnlineInfo *info = (OnWentOnlineInfo *) user_data; + + /* Now it's really time to callback to the caller. If going online didn't succeed, + * err will be set. We don't free it, Tinymail does that! If a cancel happened, + * canceled will be set. Etcetera etcetera. */ + + if (info->callback) { + info->callback (canceled, err, info->parent_window, info->account, info->user_data); + } + + /* This is our last call, we must cleanup here if we didn't yet do that */ + on_went_online_info_free (info); + + return; +} + + +static void +on_conic_device_went_online (TnyMaemoConicDevice *device, const gchar* iap_id, gboolean canceled, GError *err, gpointer user_data) +{ + OnWentOnlineInfo *info = (OnWentOnlineInfo *) user_data; + info->iap = g_strdup (iap_id); + + if (canceled || err || !info->account) { + + /* If there's a problem or if there's no account (then that's it for us, we callback + * the caller's callback now. He'll have to handle err or canceled, of course. + * We are not really online, as the account is not really online here ... */ + + /* We'll use the err and the canceled of this cb. TnyMaemoConicDevice delivered us + * this info. We don't cleanup err, Tinymail does that! */ + + if (info->callback) { + + /* info->account can be NULL here, this means that the user did not + * provide a nice account instance. We'll assume that the user knows + * what he's doing and is happy with just the device going online. + * + * We can't do magic, we don't know what account the user wants to + * see going online. So just the device goes online, end of story */ + + info->callback (canceled, err, info->parent_window, info->account, info->user_data); + } + + } else if (info->account) { + + /* If there's no problem and if we have an account, we'll put the account + * online too. When done, the callback of bringing the account online + * will callback the caller's callback. This is the most normal case. */ + + info->device = TNY_DEVICE (g_object_ref (device)); + + tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (info->account), TRUE, + on_account_went_online, info); + + /* The on_account_went_online cb frees up the info, go look if you + * don't believe me! (so we return here) */ + + return; + } + + /* We cleanup if we are not bringing the account online too */ + on_went_online_info_free (info); + + return; +} + +void +modest_platform_connect_and_perform (GtkWindow *parent_window, + gboolean force, + TnyAccount *account, + ModestConnectedPerformer callback, + gpointer user_data) +{ + gboolean device_online; + TnyDevice *device; + TnyConnectionStatus conn_status; + OnWentOnlineInfo *info; + + device = modest_runtime_get_device(); + device_online = tny_device_is_online (device); + + /* If there is no account check only the device status */ + if (!account) { + + if (device_online) { + + /* We promise to instantly perform the callback, so ... */ + if (callback) { + callback (FALSE, NULL, parent_window, account, user_data); + } + + } else { + + info = g_slice_new0 (OnWentOnlineInfo); + + info->iap = NULL; + info->device = NULL; + info->account = NULL; + + if (parent_window) + info->parent_window = (GtkWindow *) g_object_ref (parent_window); + else + info->parent_window = NULL; + info->user_data = user_data; + info->callback = callback; + + tny_maemo_conic_device_connect_async (TNY_MAEMO_CONIC_DEVICE (device), NULL, + force, on_conic_device_went_online, + info); + + /* We'll cleanup in on_conic_device_went_online */ + } + + /* The other code has no more reason to run. This is all that we can do for the + * caller (he should have given us a nice and clean account instance!). We + * can't do magic, we don't know what account he intends to bring online. So + * we'll just bring the device online (and await his false bug report). */ + + return; + } + + + /* Return if the account is already connected */ + + conn_status = tny_account_get_connection_status (account); + if (device_online && conn_status == TNY_CONNECTION_STATUS_CONNECTED) { + + /* We promise to instantly perform the callback, so ... */ + if (callback) { + callback (FALSE, NULL, parent_window, account, user_data); + } + + return; + } + + /* Else, we are in a state that requires that we go online before we + * call the caller's callback. */ + + info = g_slice_new0 (OnWentOnlineInfo); + + info->device = NULL; + info->iap = NULL; + info->account = TNY_ACCOUNT (g_object_ref (account)); + + if (parent_window) + info->parent_window = (GtkWindow *) g_object_ref (parent_window); + else + info->parent_window = NULL; + + /* So we'll put the callback away for later ... */ + + info->user_data = user_data; + info->callback = callback; + + if (!device_online) { + + /* If also the device is offline, then we connect both the device + * and the account */ + + tny_maemo_conic_device_connect_async (TNY_MAEMO_CONIC_DEVICE (device), NULL, + force, on_conic_device_went_online, + info); + + } else { + + /* If the device is online, we'll just connect the account */ + + tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (account), TRUE, + on_account_went_online, info); + } + + /* The info gets freed by on_account_went_online or on_conic_device_went_online + * in both situations, go look if you don't believe me! */ + + return; +} + +void +modest_platform_connect_if_remote_and_perform (GtkWindow *parent_window, + gboolean force, + TnyFolderStore *folder_store, + ModestConnectedPerformer callback, + gpointer user_data) +{ + TnyAccount *account = NULL; + + if (!folder_store) { + /* We promise to instantly perform the callback, so ... */ + if (callback) { + callback (FALSE, NULL, parent_window, NULL, user_data); + } + return; + + /* Original comment: Maybe it is something local. */ + /* PVH's comment: maybe we should KNOW this in stead of assuming? */ + + } else if (TNY_IS_FOLDER (folder_store)) { + /* Get the folder's parent account: */ + account = tny_folder_get_account (TNY_FOLDER (folder_store)); + } else if (TNY_IS_ACCOUNT (folder_store)) { + /* Use the folder store as an account: */ + account = TNY_ACCOUNT (g_object_ref (folder_store)); + } + + if (tny_account_get_account_type (account) == TNY_ACCOUNT_TYPE_STORE) { + if (!TNY_IS_CAMEL_POP_STORE_ACCOUNT (account) && + !TNY_IS_CAMEL_IMAP_STORE_ACCOUNT (account)) { + + /* No need to connect a local account */ + if (callback) + callback (FALSE, NULL, parent_window, account, user_data); + + goto clean; + } + } + modest_platform_connect_and_perform (parent_window, force, account, callback, user_data); + + clean: + if (account) + g_object_unref (account); +} + +static void +src_account_connect_performer (gboolean canceled, + GError *err, + GtkWindow *parent_window, + TnyAccount *src_account, + gpointer user_data) +{ + DoubleConnectionInfo *info = (DoubleConnectionInfo *) user_data; + + if (canceled || err) { + /* If there was any error call the user callback */ + info->callback (canceled, err, parent_window, src_account, info->data); + } else { + /* Connect the destination account */ + modest_platform_connect_if_remote_and_perform (parent_window, TRUE, + TNY_FOLDER_STORE (info->dst_account), + info->callback, info->data); + } + + /* Free the info object */ + g_object_unref (info->dst_account); + g_slice_free (DoubleConnectionInfo, info); +} + + +void +modest_platform_double_connect_and_perform (GtkWindow *parent_window, + gboolean force, + TnyFolderStore *folder_store, + DoubleConnectionInfo *connect_info) +{ + modest_platform_connect_if_remote_and_perform(parent_window, + force, + folder_store, + src_account_connect_performer, + connect_info); +} + +GtkWidget * +modest_platform_get_account_settings_wizard (void) +{ + ModestEasysetupWizardDialog *dialog = modest_easysetup_wizard_dialog_new (); + + return GTK_WIDGET (dialog); +} + +ModestConnectedVia +modest_platform_get_current_connection (void) +{ + TnyDevice *device = NULL; + ModestConnectedVia retval = MODEST_CONNECTED_VIA_ANY; + + device = modest_runtime_get_device (); + + if (!tny_device_is_online (device)) + return MODEST_CONNECTED_VIA_ANY; + +#ifdef MODEST_HAVE_CONIC + /* Get iap id */ + const gchar *iap_id = tny_maemo_conic_device_get_current_iap_id (TNY_MAEMO_CONIC_DEVICE (device)); + if (iap_id) { + ConIcIap *iap = tny_maemo_conic_device_get_iap ( + TNY_MAEMO_CONIC_DEVICE (device), iap_id); + const gchar *bearer_type = con_ic_iap_get_bearer_type (iap); + if (bearer_type) { + if (!strcmp (bearer_type, CON_IC_BEARER_WLAN_INFRA) || + !strcmp (bearer_type, CON_IC_BEARER_WLAN_ADHOC) || + !strcmp (bearer_type, "WIMAX")) { + retval = MODEST_CONNECTED_VIA_WLAN_OR_WIMAX; + } else { + retval = MODEST_CONNECTED_VIA_ANY; + } + } + g_object_unref (iap); + } +#else + retval = MODEST_CONNECTED_VIA_WLAN_OR_WIMAX; /* assume WLAN (fast) internet */ +#endif /* MODEST_HAVE_CONIC */ + return retval; +} + + + +gboolean +modest_platform_check_memory_low (ModestWindow *win, + gboolean visuals) +{ + gboolean lowmem; + + /* are we in low memory state? */ + lowmem = osso_mem_in_lowmem_state () ? TRUE : FALSE; + + if (win && lowmem && visuals) + modest_platform_run_information_dialog ( + GTK_WINDOW(win), + dgettext("ke-recv","memr_ib_operation_disabled"), + TRUE); + + if (lowmem) + g_debug ("%s: low memory reached. disallowing some operations", + __FUNCTION__); + + return lowmem; +} diff --git a/src/hildon2/modest-presets.c b/src/hildon2/modest-presets.c new file mode 100644 index 0000000..ef23742 --- /dev/null +++ b/src/hildon2/modest-presets.c @@ -0,0 +1,382 @@ +/* Copyright (c) 2006, Nokia Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Nokia Corporation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include /* for strcmp */ +#include +#include +#include "modest-presets.h" +#include + +/* Include config.h so that _() works: */ +#ifdef HAVE_CONFIG_H +#include +#endif + +#define MODEST_PRESETS_KEY_NAME "Name" +#define MODEST_PRESETS_KEY_DOMAIN "Domain" +#define MODEST_PRESETS_KEY_MCC "MCC" +#define MODEST_PRESETS_KEY_INCOMING "IncomingMailServer" +#define MODEST_PRESETS_KEY_INCOMING_SECURITY "IncomingSecurity" +#define MODEST_PRESETS_KEY_OUTGOING "OutgoingMailServer" +#define MODEST_PRESETS_KEY_MAILBOX_TYPE "MailboxType" +#define MODEST_PRESETS_KEY_APOP "APOPSecureLogin" +#define MODEST_PRESETS_KEY_SECURE_SMTP "SecureSmtp" +#define MODEST_PRESETS_KEY_SMTP_PORT "SmtpPort" + + +ModestPresets* +modest_presets_new (const gchar *presetfile) +{ + ModestPresets *presets = NULL; + GError *err = NULL; + + g_return_val_if_fail (presetfile, NULL); + + presets = g_new (ModestPresets, 1); + presets->keyfile = g_key_file_new (); + + if (!presets->keyfile) { + g_printerr ("modest: cannot instantiate GKeyFile\n"); + g_free (presets); + return NULL; + } + + if (!g_key_file_load_from_file (presets->keyfile, presetfile, + G_KEY_FILE_NONE, &err)) { + g_printerr ("modest: cannot open keyfile from %s:\n %s\n", presetfile, + err ? err->message : "unknown reason"); + g_error_free (err); + g_free (presets); + return NULL; + } + + return presets; +} + +gchar** +modest_presets_get_providers (ModestPresets *self, guint mcc, + gboolean include_globals, gchar ***provider_ids) +{ + gchar **all_providers = NULL; + gchar **all_provider_ids = NULL; + gchar **filtered = NULL; + gchar **filtered_ids = NULL; + GError *err = NULL; + guint i, j, len; + + g_return_val_if_fail (self && self->keyfile, NULL); + + /* Get all the provider IDs: */ + all_provider_ids = g_key_file_get_groups (self->keyfile, NULL); + len = g_strv_length(all_provider_ids); + + /* Get the names for all these providers: */ + all_providers = g_new0(gchar*, len + 1); /* Provider names. */ + for (i=0; i != len; ++i) { + const gchar * provider_id = all_provider_ids[i]; + if(provider_id) { + gchar* name = g_key_file_get_string(self->keyfile, provider_id, + MODEST_PRESETS_KEY_NAME, NULL); + + /* Be forgiving of missing names. + * If we use NULL then we will null-terminate the array. + */ + if(!name) + name = g_strdup(""); + + all_providers[i] = name; + } + else + all_providers[i] = NULL; + }; + + /* return *all* providers? */ + /* + if (mcc == 0 && include_globals) { + *provider_ids = all_provider_ids; + return all_providers; + } + */ + + /* nope: filter them */ + + filtered = g_new0(gchar*, len + 1); /* Provider names. */ + filtered_ids = g_new0(gchar*, len + 1); /* Provider IDs */ + + for (i=0, j=0; i != len; ++i) { + + int this_mcc; + this_mcc = g_key_file_get_integer (self->keyfile, all_provider_ids[i], + MODEST_PRESETS_KEY_MCC, &err); + if (err) { + g_strfreev (all_providers); + g_strfreev (all_provider_ids); + g_strfreev (filtered); + g_strfreev (filtered_ids); + + g_printerr ("modest: error parsing keyfile: %s\n", err->message); + g_error_free (err); + + return NULL; + } + + if (this_mcc == mcc || (this_mcc == 0 && include_globals)) { + filtered[j] = all_providers[i]; + filtered_ids[j] = all_provider_ids[i]; + ++j; + filtered[j] = NULL; /* the array must be NULL-terminated */ + filtered_ids[j] = NULL; /* the array must be NULL-terminated */ + + all_providers[i] = NULL; /* g_strfreev: leave it alone */ + all_provider_ids[i] = NULL; /* g_strfreev: leave it alone */ + } + } + + g_strfreev (all_providers); + g_strfreev (all_provider_ids); + + *provider_ids = filtered_ids; + return filtered; +} + + +gchar* +modest_presets_get_server (ModestPresets *self, const gchar *provider_id, + gboolean incoming_server) +{ + g_return_val_if_fail (self && self->keyfile, NULL); + g_return_val_if_fail (provider_id, NULL); + + return g_key_file_get_string (self->keyfile, provider_id, + incoming_server ? + MODEST_PRESETS_KEY_INCOMING : + MODEST_PRESETS_KEY_OUTGOING, + NULL); +} + +gchar * +modest_presets_get_domain (ModestPresets *self, + const gchar *provider_id) +{ + g_return_val_if_fail (self && self->keyfile, NULL); + g_return_val_if_fail (provider_id, NULL); + + return g_key_file_get_string (self->keyfile, provider_id, + MODEST_PRESETS_KEY_DOMAIN, + NULL); +} + + + + +ModestProtocolType +modest_presets_get_info_server_type (ModestPresets *self, + const gchar *provider_id, + gboolean incoming_server) +{ + ModestProtocolType protocol_type = MODEST_PROTOCOL_REGISTRY_TYPE_INVALID; + ModestProtocolRegistry *protocol_registry; + ModestProtocol *protocol; + gchar *val = NULL; + + g_return_val_if_fail (self && self->keyfile, 0); + protocol_registry = modest_runtime_get_protocol_registry (); + + if (incoming_server) { + val = g_key_file_get_string (self->keyfile, provider_id, + MODEST_PRESETS_KEY_INCOMING, NULL); + if (!val) + return protocol_type; + + g_free (val); + val = g_key_file_get_string (self->keyfile, provider_id, + MODEST_PRESETS_KEY_MAILBOX_TYPE,NULL); + + protocol = modest_protocol_registry_get_protocol_by_name (protocol_registry, MODEST_PROTOCOL_REGISTRY_STORE_PROTOCOLS, val); + if (protocol == NULL) + return protocol_type; + protocol_type = modest_protocol_get_type_id (protocol); + } else { + val = g_key_file_get_string (self->keyfile, provider_id, + MODEST_PRESETS_KEY_OUTGOING, NULL); + if (!val) + return protocol_type; + + protocol_type = MODEST_PROTOCOLS_TRANSPORT_SMTP; + } + g_free (val); + + /* debug: */ +/* g_message ("provider id: %s, server type: %d", provider_id, info); */ + return protocol_type; +} + + + +ModestProtocolType +modest_presets_get_info_server_security (ModestPresets *self, const gchar *provider_id, + gboolean incoming_server) +{ + ModestProtocolType protocol_type = MODEST_PROTOCOLS_CONNECTION_NONE; + gchar *val = NULL; + + g_return_val_if_fail (self && self->keyfile, MODEST_PROTOCOLS_CONNECTION_NONE); + + if (incoming_server) { + val = g_key_file_get_string (self->keyfile, provider_id, + MODEST_PRESETS_KEY_INCOMING, NULL); + if (val) { + g_free (val); + + val = g_key_file_get_string (self->keyfile, provider_id, + MODEST_PRESETS_KEY_INCOMING_SECURITY, NULL); + if (val && ((strcmp (val, "1") == 0) || (strcmp (val, "2") == 0))) { + protocol_type = MODEST_PROTOCOLS_CONNECTION_SSL; + } + g_free (val); + } + } else /* outgoing: */ { + val = g_key_file_get_string (self->keyfile, provider_id, + MODEST_PRESETS_KEY_OUTGOING, NULL); + if (val) { + g_free (val); + + val = g_key_file_get_string (self->keyfile, provider_id, + MODEST_PRESETS_KEY_SECURE_SMTP, NULL); + /* printf("debug: %s: provider_id=%s, secure-smtp val=%s\n", __FUNCTION__, provider_id, val); */ + if (val && strcmp(val,"true") == 0) + protocol_type = MODEST_PROTOCOLS_CONNECTION_SSL; + g_free(val); + } + } + + return protocol_type; +} + +gboolean +modest_presets_get_info_server_use_alternate_port (ModestPresets *self, const gchar *provider_id, + gboolean incoming_server) +{ + gboolean result = FALSE; + gchar *val = NULL; + + g_return_val_if_fail (self && self->keyfile, MODEST_PROTOCOLS_CONNECTION_NONE); + + if (incoming_server) { + val = g_key_file_get_string (self->keyfile, provider_id, + MODEST_PRESETS_KEY_INCOMING, NULL); + if (val) { + g_free (val); + + val = g_key_file_get_string (self->keyfile, provider_id, + MODEST_PRESETS_KEY_INCOMING_SECURITY, NULL); + if (val && (strcmp (val, "2") == 0)) { + result = TRUE; + } + g_free (val); + } + } + + return result; +} + +ModestProtocolType +modest_presets_get_info_server_auth (ModestPresets *self, const gchar *provider_id, + gboolean incoming_server) +{ + ModestProtocolType protocol_type = MODEST_PROTOCOLS_AUTH_NONE; + gchar *val = NULL; + + g_return_val_if_fail (self && self->keyfile, MODEST_PROTOCOLS_AUTH_NONE); + + if (incoming_server) { + val = g_key_file_get_string (self->keyfile, provider_id, + MODEST_PRESETS_KEY_INCOMING, NULL); + if (val) { + g_free (val); + val = g_key_file_get_string (self->keyfile, provider_id, + MODEST_PRESETS_KEY_APOP, NULL); + if (val && strcmp(val, "true") == 0) + protocol_type = MODEST_PROTOCOLS_AUTH_PASSWORD; + g_free(val); + + } + } else /* outgoing: */ { + val = g_key_file_get_string (self->keyfile, provider_id, + MODEST_PRESETS_KEY_OUTGOING, NULL); + if (val) { + g_free (val); + + val = g_key_file_get_string (self->keyfile, provider_id, + MODEST_PRESETS_KEY_SECURE_SMTP, NULL); + /* printf("debug: %s: provider_id=%s, secure-smtp val=%s\n", __FUNCTION__, provider_id, val); */ + if (val && strcmp(val,"true") == 0) + protocol_type = MODEST_PROTOCOLS_AUTH_PASSWORD; + g_free(val); + } + } + + return protocol_type; +} + +/* + * at the moment, this only for mac.com, which have a special SMTP port + */ +guint +modest_presets_get_port (ModestPresets *self, const gchar* provider_id, + gboolean incoming_server) +{ + guint port; + + g_return_val_if_fail (self && self->keyfile, 0); + + if (incoming_server) + port = 0; /* not used yet */ + else + port = (guint)g_key_file_get_integer (self->keyfile, provider_id, + MODEST_PRESETS_KEY_SMTP_PORT, NULL); + + return port; +} + + + + + +void +modest_presets_destroy (ModestPresets *self) +{ + if (!self) + return; + + g_key_file_free (self->keyfile); + self->keyfile = NULL; + + g_free (self); +} diff --git a/src/hildon2/modest-presets.h b/src/hildon2/modest-presets.h new file mode 100644 index 0000000..96e90d1 --- /dev/null +++ b/src/hildon2/modest-presets.h @@ -0,0 +1,187 @@ +/* Copyright (c) 2006, Nokia Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Nokia Corporation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __MODEST_PRESETS_H__ +#define __MODEST_PRESETS_H__ + +#include +#include + +struct _ModestPresets { +/* private data: don't touch */ + GKeyFile *keyfile; +}; +typedef struct _ModestPresets ModestPresets; + + +/** + * modest_presets_new: + * @presetfile: the full path to the file with presets (in GKeyFile format) + * + * make a new ModestPresets instance + * + * Returns: a new ModestPresets instance, or NULL in case of error. + */ +ModestPresets* modest_presets_new (const gchar *presetfile); + + +/** + * modest_presets_get_providers: + * @self: a valid ModestPresets instance + * @mcc: limit the search to providers with this mcc (Mobile Country Code), + * or 0 to get all + * @include_globals: include (global) providers without MCC (such as GMail, Yahoo) + * @provider_ids: Output parameter, which will be set to a newly allocated array of strings, or NULL in case of error. + * + * get a list of providers matching certian criteria + * + * Returns: The provider names, as a newly allocated array of strings, or NULL in case of error + * should be freed with g_strfreev + * + **/ +gchar ** modest_presets_get_providers (ModestPresets *self, guint mcc, + gboolean include_globals, gchar ***provider_ids); + +/** + * modest_presets_get_server: + * @self: a valid ModestPresets instance + * @provider_id: ID of the provider + * @incoming_server: get the incoming mailserver if TRUE, get the + * outgoing server otherwise + * + * get the name of a incoming or outgoing mailserver + * + * Returns: a newly allocated string with the servername, or NULL in case + * of error, or server not found. (FIXME). Note that if the (incoming) server uses a + * non-standard port, the port number is appended to the name, eg. pop.foo.fi:995 + */ +gchar * modest_presets_get_server (ModestPresets *self, + const gchar *provider_id, + gboolean incoming_server); + +/** + * modest_presets_get_domain: + * @self: a valid ModestPresets instance + * @provider_id: ID of the provider + * + * Get the name of the most-used domain for theis provider. For instance. hotmail.com + * + * Returns: a newly allocated string with the domain name, or NULL in case + * of error. + */ +gchar * modest_presets_get_domain (ModestPresets *self, + const gchar *provider_id); + +/** + * modest_presets_get_info_server_type: + * @self: a valid ModestPresets instance + * @provider_id: ID of the provider + * @incoming_server: get the incoming mailserver if TRUE, get the + * outgoing server otherwise + * + * get information about some incoming or outgoing mailserver + * + * Returns: a #ModestProtocolType with the required information + */ +ModestProtocolType modest_presets_get_info_server_type (ModestPresets *self, + const gchar *provider_id, + gboolean incoming_server); + +/** + * modest_presets_get_info_server_security: + * @self: a valid ModestPresets instance + * @provider_id: ID of the provider + * @incoming_server: get the incoming mailserver if TRUE, get the + * outgoing server otherwise + * + * get information about some incoming or outgoing mailserver + * + * Returns: #ModestProtocolType with server auth + */ +ModestProtocolType modest_presets_get_info_server_auth (ModestPresets *self, + const gchar *provider_id, + gboolean incoming_server); + +/** + * modest_presets_get_info_server_security: + * @self: a valid ModestPresets instance + * @provider_id: ID of the provider + * @incoming_server: get the incoming mailserver if TRUE, get the + * outgoing server otherwise + * + * get information about some incoming or outgoing mailserver + * + * Returns: #ModestProtocolType with server security + */ +ModestProtocolType modest_presets_get_info_server_security (ModestPresets *self, + const gchar *provider_id, + gboolean incoming_server); + +/** + * modest_presets_get_info_server_security: + * @self: a valid ModestPresets instance + * @provider_id: ID of the provider + * @incoming_server: get the incoming mailserver if TRUE, get the + * outgoing server otherwise + * + * get information about some incoming or outgoing mailserver + * + * Returns: %TRUE if we should use the alternate port. + */ +gboolean modest_presets_get_info_server_use_alternate_port (ModestPresets *self, + const gchar *provider_id, + gboolean incoming_server); + + +/** + * modest_presets_get_port: + * @self: a valid ModestPresets instance + * @provider_id: ID of the provider + * @incoming_server: get port# for the incoming mailserver if TRUE, for the outgoing server otherwise + * + * Returns: the specific port number for some provider + * function return 0 if the normal port number is valid + * + */ +guint modest_presets_get_port (ModestPresets *self, const gchar* provider_id, + gboolean incoming_server); + + +/** + * modest_presets_destroy: + * @self: a valid ModestPresets instance (ie. must not be NULL) + * + * destroy ModestPresets instance; this is required after you're done with it. + */ +void modest_presets_destroy (ModestPresets *self); + + +#endif /*__MODEST_PRESETS__*/ + + diff --git a/src/hildon2/modest-signature-editor-dialog.c b/src/hildon2/modest-signature-editor-dialog.c new file mode 100644 index 0000000..a0a190d --- /dev/null +++ b/src/hildon2/modest-signature-editor-dialog.c @@ -0,0 +1,232 @@ +/* Copyright (c) 2006, Nokia Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Nokia Corporation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "modest-signature-editor-dialog.h" +#include "widgets/modest-ui-constants.h" +#include "modest-hildon-includes.h" +#include "widgets/modest-serversecurity-combo-box.h" +#include "widgets/modest-secureauth-combo-box.h" +#include "widgets/modest-validating-entry.h" +#include "modest-runtime.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +G_DEFINE_TYPE (ModestSignatureEditorDialog, modest_signature_editor_dialog, GTK_TYPE_DIALOG); + +#define SIGNATURE_EDITOR_DIALOG_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), MODEST_TYPE_SIGNATURE_EDITOR_DIALOG, ModestSignatureEditorDialogPrivate)) + +typedef struct _ModestSignatureEditorDialogPrivate ModestSignatureEditorDialogPrivate; + +struct _ModestSignatureEditorDialogPrivate +{ + GtkWidget *checkbox_use; + GtkWidget *label; + GtkWidget *scrolledwindow; + GtkWidget *textview; +}; + +static void +modest_signature_editor_dialog_get_property (GObject *object, guint property_id, + GValue *value, GParamSpec *pspec) +{ + switch (property_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +modest_signature_editor_dialog_set_property (GObject *object, guint property_id, + const GValue *value, GParamSpec *pspec) +{ + switch (property_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +modest_signature_editor_dialog_dispose (GObject *object) +{ + if (G_OBJECT_CLASS (modest_signature_editor_dialog_parent_class)->dispose) + G_OBJECT_CLASS (modest_signature_editor_dialog_parent_class)->dispose (object); +} + +static void +modest_signature_editor_dialog_finalize (GObject *object) +{ + G_OBJECT_CLASS (modest_signature_editor_dialog_parent_class)->finalize (object); +} + +static void +modest_signature_editor_dialog_class_init (ModestSignatureEditorDialogClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (ModestSignatureEditorDialogPrivate)); + + object_class->get_property = modest_signature_editor_dialog_get_property; + object_class->set_property = modest_signature_editor_dialog_set_property; + object_class->dispose = modest_signature_editor_dialog_dispose; + object_class->finalize = modest_signature_editor_dialog_finalize; +} + +static void +enable_widgets (ModestSignatureEditorDialog *self) +{ + ModestSignatureEditorDialogPrivate *priv = + SIGNATURE_EDITOR_DIALOG_GET_PRIVATE (self); + + const gboolean enable = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->checkbox_use)); + gtk_widget_set_sensitive (priv->label, enable); + gtk_widget_set_sensitive (priv->scrolledwindow, enable); + gtk_text_view_set_editable (GTK_TEXT_VIEW (priv->textview), enable); +} + +static void +on_toggle_button_changed (GtkToggleButton *togglebutton, gpointer user_data) +{ + ModestSignatureEditorDialog *self = MODEST_SIGNATURE_EDITOR_DIALOG (user_data); + enable_widgets (self); +} + +static void +modest_signature_editor_dialog_init (ModestSignatureEditorDialog *self) +{ + ModestSignatureEditorDialogPrivate *priv = + SIGNATURE_EDITOR_DIALOG_GET_PRIVATE (self); + + gtk_window_set_title (GTK_WINDOW (self), _("mcen_ti_email_signatures_edit_title")); + + GtkWidget *box = GTK_DIALOG(self)->vbox; /* gtk_vbox_new (FALSE, MODEST_MARGIN_HALF); */ + gtk_container_set_border_width (GTK_CONTAINER (box), MODEST_MARGIN_HALF); + + priv->checkbox_use = gtk_check_button_new_with_label ( + _("mcen_fi_email_signatures_use_signature")); + gtk_box_pack_start (GTK_BOX (box), priv->checkbox_use, FALSE, FALSE, MODEST_MARGIN_HALF); + gtk_widget_show (priv->checkbox_use); + + g_signal_connect (G_OBJECT (priv->checkbox_use), "toggled", + G_CALLBACK (on_toggle_button_changed), self); + + + priv->label = gtk_label_new (""); /* Set in modest_signature_editor_dialog_set_settings(). */ + gtk_box_pack_start (GTK_BOX (box), priv->label, FALSE, FALSE, MODEST_MARGIN_HALF); + gtk_widget_show (priv->label); + + priv->scrolledwindow = gtk_scrolled_window_new (NULL, NULL); + gtk_container_set_border_width (GTK_CONTAINER (priv->scrolledwindow), MODEST_MARGIN_DEFAULT); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolledwindow), + GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->scrolledwindow), GTK_SHADOW_IN); + gtk_box_pack_start (GTK_BOX (box), priv->scrolledwindow, FALSE, FALSE, MODEST_MARGIN_HALF); + gtk_widget_show (priv->scrolledwindow); + + priv->textview = gtk_text_view_new (); + gtk_container_add (GTK_CONTAINER (priv->scrolledwindow), priv->textview); + gtk_widget_show (priv->textview); + GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->textview)); + gtk_text_buffer_set_text (buffer, "--\n", -1); /* Default, as per the UI spec. */ + + /* Add the buttons: */ + gtk_dialog_add_button (GTK_DIALOG (self), _("mcen_bd_dialog_ok"), GTK_RESPONSE_OK); + gtk_dialog_add_button (GTK_DIALOG (self), _("mcen_bd_dialog_cancel"), GTK_RESPONSE_CANCEL); + + gtk_widget_show (box); + gtk_widget_set_size_request (GTK_WIDGET (self), 480, -1); + + /* When this window is shown, hibernation should not be possible, + * because there is no sensible way to save the state: */ + modest_window_mgr_prevent_hibernation_while_window_is_shown ( + modest_runtime_get_window_mgr (), GTK_WINDOW (self)); + + hildon_help_dialog_help_enable (GTK_DIALOG(self), "applications_email_signatureeditor", + modest_maemo_utils_get_osso_context()); +} + +ModestSignatureEditorDialog* +modest_signature_editor_dialog_new (void) +{ + return g_object_new (MODEST_TYPE_SIGNATURE_EDITOR_DIALOG, NULL); +} + +void +modest_signature_editor_dialog_set_settings ( + ModestSignatureEditorDialog *window, gboolean use_signature, const gchar* signature, + const gchar* account_title) +{ + ModestSignatureEditorDialogPrivate *priv = + SIGNATURE_EDITOR_DIALOG_GET_PRIVATE (window); + + /* This causes a warning because of the %s in the translation, but not in the original string: */ + gchar* label_text = g_strdup_printf (_("mcen_ia_email_signatures_edit_dlg_label"), + account_title); + gtk_label_set_text (GTK_LABEL (priv->label), label_text); + gtk_label_set_ellipsize (GTK_LABEL (priv->label), PANGO_ELLIPSIZE_END); + g_free (label_text); + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->checkbox_use), use_signature); + + GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->textview)); + if (signature) + gtk_text_buffer_set_text (buffer, signature, -1); + else + gtk_text_buffer_set_text (buffer, "--\n", -1); /* Default, as per the UI spec. */ + + enable_widgets (window); +} + +/* + * The result must be freed with g_free(). */ +gchar* +modest_signature_editor_dialog_get_settings ( + ModestSignatureEditorDialog *window, gboolean* use_signature) +{ + ModestSignatureEditorDialogPrivate *priv = + SIGNATURE_EDITOR_DIALOG_GET_PRIVATE (window); + + g_assert(use_signature); + + *use_signature = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->checkbox_use)); + + GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->textview)); + + GtkTextIter start, end; + gtk_text_buffer_get_bounds (buffer, &start, &end); + return gtk_text_buffer_get_text (buffer, &start, &end, TRUE); +} diff --git a/src/hildon2/modest-signature-editor-dialog.h b/src/hildon2/modest-signature-editor-dialog.h new file mode 100644 index 0000000..e9e22ea --- /dev/null +++ b/src/hildon2/modest-signature-editor-dialog.h @@ -0,0 +1,85 @@ +/* Copyright (c) 2006, Nokia Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Nokia Corporation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __MODEST_MAEMO_SIGNATURE_EDITOR_DIALOG +#define __MODEST_MAEMO_SIGNATURE_EDITOR_DIALOG + +#include +#include /* For ModestServerAccountData */ +#include +#include + +G_BEGIN_DECLS + +#define MODEST_TYPE_SIGNATURE_EDITOR_DIALOG modest_signature_editor_dialog_get_type() + +#define MODEST_SIGNATURE_EDITOR_DIALOG(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + MODEST_TYPE_SIGNATURE_EDITOR_DIALOG, ModestSignatureEditorDialog)) + +#define MODEST_SIGNATURE_EDITOR_DIALOG_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), \ + MODEST_TYPE_SIGNATURE_EDITOR_DIALOG, ModestSignatureEditorDialogClass)) + +#define MODEST_IS_SIGNATURE_EDITOR_DIALOG(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + MODEST_TYPE_SIGNATURE_EDITOR_DIALOG)) + +#define MODEST_IS_SIGNATURE_EDITOR_DIALOG_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + MODEST_TYPE_SIGNATURE_EDITOR_DIALOG)) + +#define MODEST_SIGNATURE_EDITOR_DIALOG_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + MODEST_TYPE_SIGNATURE_EDITOR_DIALOG, ModestSignatureEditorDialogClass)) + +typedef struct { + GtkDialog parent; + +} ModestSignatureEditorDialog; + +typedef struct { + GtkDialogClass parent_class; +} ModestSignatureEditorDialogClass; + +GType modest_signature_editor_dialog_get_type (void); + +ModestSignatureEditorDialog* modest_signature_editor_dialog_new (void); + +void modest_signature_editor_dialog_set_settings ( + ModestSignatureEditorDialog *window, gboolean use_signature, const gchar* signature, + const gchar* account_title); + +gchar* modest_signature_editor_dialog_get_settings ( + ModestSignatureEditorDialog *window, gboolean* use_signature); + + +G_END_DECLS + +#endif /* __MODEST_MAEMO_CONNECTION_SPECIFIC_SMTP_WINDOW */ diff --git a/src/hildon2/modest.conf b/src/hildon2/modest.conf new file mode 100644 index 0000000..cda0c61 --- /dev/null +++ b/src/hildon2/modest.conf @@ -0,0 +1,5 @@ + + + /home/user/.modest + + diff --git a/src/hildon2/modest.desktop.in b/src/hildon2/modest.desktop.in new file mode 100644 index 0000000..20f98ed --- /dev/null +++ b/src/hildon2/modest.desktop.in @@ -0,0 +1,27 @@ +[Desktop Entry] +Encoding=UTF-8 +Version=@PACKAGE_VERSION@ +Type=Application +Name=mcen_ap_name +Comment=mcen_ap_name_thumb +Exec=@prefix@/bin/modest + +X-Osso-Service=com.nokia.modest +X-Osso-Type=application/x-executable + +Icon=qgn_list_messagin +X-Window-Icon=qgn_list_messagin +X-Window-Icon-Dimmed=qgn_list_messagin + +StartupWMClass=modest + +MimeType=application/x-executable; + +# +# below to be activated when dbus/send to functionality works +# +X-Osso-URI-Actions=mailto; +[X-Osso-URI-Action Handler mailto] +Method=MailTo +Name=uri_link_compose_email +TranslationDomain=osso-uri diff --git a/src/hildon2/pixmaps/modest-icon.png b/src/hildon2/pixmaps/modest-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..5d18b4911635a228b4c13862212d36eea47e3617 GIT binary patch literal 1189 zcmV;W1X}xvP)&LX8U+ z#>B*(iA&v(kQkzom9P*EA`lHYf?>BsbR-+J6~(KCH#GQ%S=Zj2{6smeKZ z>aV}*)Tx61nuz`T_uDv*iQ^b+?Xm&Z+9kR$ZlS-D8e^7>1Ms3KVtjo3p;pzK+@g&| z>lkBVw2smDYUle04}65zZnx=lI%G1L2ZES+0R4~vTE{3E(Q37Dq?8;zdh`J-Vw8%J z%G_UNuT_im%G3niZWqUOUAo;ahYlUOCkuHW5Ty)CE}eYAcXr|!uG2d=FO$g-h9PIq zo@3(pL58RIE(1(${TvfJmu10Tpz)rBZ}pNNsG4uYb5s^y~zgEpIK#OBda~hB>thTR)Ak3SnCKHx6KDj**+( zP4L$jIHrj}lWQHpyY>lJrzfe^#t6d@00*F0EHYdjrdTTR*}*S~B9j35CCo2xqaS&n zX!EB_nC7P5L8dqH$HwoN&2H=4+c!_Mdi5%*!&QpKBE}dTV+=tMFj%QjtBnCJ*MI#g znN7~1*Y2fTd4;$=y#z?6HgaR=Jt7m}MVI^buBfnX-57&|6@nl@DTQZ@!F65o`5dDo zA+2VMRqc!Ypg6mkZgnqO88Y>gNuuCzv+^!df!n=Epc5blw=p^rlF#RHT^A`Oj@B9> z1itT6DwkPbp22riKc*ADj?^Hf!f$@xx6fv`(`E#v4aN51zP%Y&DU5EVR4(EBK0=5D z2*7b1eBY<2Cz2pppzb}1R0i8ThimG6-%MsFTF2PfQfeRR+i&~Nk;@nG;|7l7^tO00 z(1N5}C;I(cJi$+APN0+_)kxSFF+el*GyoZOo^*UIVVd7bCOQWvw_#-?0a8jO6=3cL zUMD%9?Ajd=cewQXd4l$$ZA(9c6{&d&N#f}2X8cq@;nEw}4R3;9=q=0`JgxP71&GnN zzQOC?ACJnW=h@&~<>KVe1jPvuJ=dki8ZO`Y5!drrAJ<`^XZPkV@M!l{W{WT35ADX7 zP+YFtK`d9u6 zGj}_HK#Y%%Tdg(57#7- + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/hildon2/ui/modest-msg-edit-window-ui.xml b/src/hildon2/ui/modest-msg-edit-window-ui.xml new file mode 100644 index 0000000..1386fba --- /dev/null +++ b/src/hildon2/ui/modest-msg-edit-window-ui.xml @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/hildon2/ui/modest-msg-view-window-ui.xml b/src/hildon2/ui/modest-msg-view-window-ui.xml new file mode 100644 index 0000000..244140e --- /dev/null +++ b/src/hildon2/ui/modest-msg-view-window-ui.xml @@ -0,0 +1,119 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/modest-ui-actions.c b/src/modest-ui-actions.c index 2239f56..ef2b24c 100644 --- a/src/modest-ui-actions.c +++ b/src/modest-ui-actions.c @@ -71,7 +71,11 @@ #include "modest-text-utils.h" #ifdef MODEST_HAVE_EASYSETUP +#ifdef MODEST_TOOLKIT_HILDON2 +#include "modest-easysetup-wizard-dialog.h" +#else #include "easysetup/modest-easysetup-wizard-dialog.h" +#endif #endif /* MODEST_HAVE_EASYSETUP */ #include diff --git a/src/widgets/Makefile.am b/src/widgets/Makefile.am index ccdef8f..a4d31d1 100644 --- a/src/widgets/Makefile.am +++ b/src/widgets/Makefile.am @@ -9,7 +9,7 @@ INCLUDES=\ $(MODEST_HILDON_HELP_CFLAGS) \ $(MODEST_MOZEMBED_CFLAGS) \ $(MODEST_HILDON_NOTIFY_CFLAGS) \ - -I ${top_srcdir}/src/$(MODEST_PLATFORM) \ + -I ${top_srcdir}/src/$(MODEST_TOOLKIT_DIR) \ -I ${top_srcdir}/src \ -DPREFIX=\"@prefix@\" \ -DPIXMAP_PREFIX=\"${datadir}/pixmaps/modest/\" \ diff --git a/tests/Makefile.am b/tests/Makefile.am index fec105b..c9f6ffe 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -30,7 +30,7 @@ INCLUDES=\ $(MODEST_GSTUFF_CFLAGS)\ $(MODEST_LIBTINYMAIL_GNOME_DESKTOP_CFLAGS) \ $(MODEST_LIBTINYMAIL_MAEMO_CFLAGS) \ - -I$(MODEST_PLATFORM) \ + -I$(MODEST_TOOLKIT_DIR) \ -I${top_srcdir}/src \ -DPREFIX=\"@prefix@\" \ -DMODESTLOCALEDIR=\""$(modestlocaledir)"\" @@ -39,7 +39,7 @@ objects=\ $(MODEST_GSTUFF_LIBS) \ $(MODEST_LIBTINYMAIL_GNOME_DESKTOP_LIBS) \ $(MODEST_LIBTINYMAIL_MAEMO_LIBS) \ - ${top_srcdir}/src/$(MODEST_PLATFORM)/libmodest-ui.la \ + ${top_srcdir}/src/$(MODEST_TOOLKIT_DIR)/libmodest-ui.la \ ${top_srcdir}/src/widgets/libmodest-widgets.la \ ${top_srcdir}/src/dbus_api/libmodest-dbus-api.la \ ${top_srcdir}/src/modest-runtime.o \ diff --git a/tests/dbus_api/Makefile.am b/tests/dbus_api/Makefile.am index 01fa744..bc1dd67 100644 --- a/tests/dbus_api/Makefile.am +++ b/tests/dbus_api/Makefile.am @@ -3,7 +3,7 @@ INCLUDES=\ $(MODEST_GSTUFF_CFLAGS)\ $(MODEST_LIBTINYMAIL_GNOME_DESKTOP_CFLAGS) \ $(MODEST_LIBTINYMAIL_MAEMO_CFLAGS) \ - -I$(MODEST_PLATFORM) \ + -I$(MODEST_TOOLKIT_DIR) \ -I${top_srcdir}/src \ -I$(top_srcdir) \ -DPREFIX=\"@prefix@\" \ -- 1.7.9.5