diff options
Diffstat (limited to 'plugins')
64 files changed, 3223 insertions, 93 deletions
diff --git a/plugins/addressbook-file/ChangeLog b/plugins/addressbook-file/ChangeLog index c44ddb846c..167e44dc61 100644 --- a/plugins/addressbook-file/ChangeLog +++ b/plugins/addressbook-file/ChangeLog @@ -1,3 +1,11 @@ +2008-07-31 Matthew Barnes <mbarnes@redhat.com> + + ** Fixes part of bug #545568 + + * org-gnome-addressbook-file.eplug.xml: + Add "system_plugin=true" so it's not shown in the Plugin Manager. + This plugin is not designed to be disabled by the user. + 2006-02-28 Christian Kellner <gicmo@gnome.org> * addressbook-file.c: fixing a leak. diff --git a/plugins/addressbook-file/org-gnome-addressbook-file.eplug.xml b/plugins/addressbook-file/org-gnome-addressbook-file.eplug.xml index 4b041f8c45..b9fe52e341 100644 --- a/plugins/addressbook-file/org-gnome-addressbook-file.eplug.xml +++ b/plugins/addressbook-file/org-gnome-addressbook-file.eplug.xml @@ -4,7 +4,8 @@ type="shlib" location="@PLUGINDIR@/liborg-gnome-addressbook-file@SOEXT@" id="org.gnome.evolution.addressbook.file" - _name="Local Address Books"> + _name="Local Address Books" + system_plugin="true"> <author name="Sivaiah Nallagatla" email="snallagatla@novell.com"/> <_description>Provides core functionality for local address books.</_description> <hook class="org.gnome.evolution.addressbook.config:1.0"> diff --git a/plugins/backup-restore/ChangeLog b/plugins/backup-restore/ChangeLog index 2abd006846..3dbab34a81 100644 --- a/plugins/backup-restore/ChangeLog +++ b/plugins/backup-restore/ChangeLog @@ -1,3 +1,10 @@ +2008-08-01 Matthew Barnes <mbarnes@redhat.com> + + ** Fixes bug #543754 + + * org-gnome-backup-restore.error.xml: + Reword "insufficient-permissions" error message. + 2008-07-03 Roger Zauner <roger@eskimo.com> ** Fix for bug #540400 diff --git a/plugins/backup-restore/org-gnome-backup-restore.error.xml b/plugins/backup-restore/org-gnome-backup-restore.error.xml index 6085a1196d..6b3bb8ad52 100644 --- a/plugins/backup-restore/org-gnome-backup-restore.error.xml +++ b/plugins/backup-restore/org-gnome-backup-restore.error.xml @@ -19,6 +19,6 @@ </error> <error id="insufficient-permissions" type="error"> <_title>Insufficient Permissions</_title> - <_secondary>The selected folder does not have enough permissions to create the file</_secondary> + <_secondary>The selected folder is not writable.</_secondary> </error> </error-list> diff --git a/plugins/bbdb/ChangeLog b/plugins/bbdb/ChangeLog index 18b7b6afbc..53d5e13f7e 100644 --- a/plugins/bbdb/ChangeLog +++ b/plugins/bbdb/ChangeLog @@ -1,3 +1,18 @@ +2008-08-07 Paul Bolle <pebolle@tiscali.nl> + + ** Fixes bug #546785 + + * bbdb.c: (e_plugin_lib_enable): The bbdb plugin is noisy when + enabled: + "BBDB spinning up..." + Make this plugin just as polite as google-account-setup. + +2008-07-28 Lucian Langa <lucilanga@gnome.org> + + ** See bug #504417 + + * bbdb.c: - Preferences window cut off + 2008-01-10 Milan Crha <mcrha@redhat.com> ** Fix for bug #496402 diff --git a/plugins/bbdb/bbdb.c b/plugins/bbdb/bbdb.c index feac342232..98db91fb99 100644 --- a/plugins/bbdb/bbdb.c +++ b/plugins/bbdb/bbdb.c @@ -49,6 +49,8 @@ #include "bbdb.h" +#define d(x) + /* Plugin hooks */ int e_plugin_lib_enable (EPluginLib *ep, int enable); void bbdb_handle_reply (EPlugin *ep, EMEventTargetMessage *target); @@ -119,7 +121,7 @@ e_plugin_lib_enable (EPluginLib *ep, int enable) { /* Start up the plugin. */ if (enable) { - fprintf (stderr, "BBDB spinning up...\n"); + d(fprintf (stderr, "BBDB spinning up...\n")); if (bbdb_check_gaim_enabled ()) bbdb_sync_buddy_list_check (); @@ -562,7 +564,7 @@ bbdb_page_factory (EPlugin *ep, EConfigHookItemFactoryData *hook_data) gtk_box_pack_start (GTK_BOX (hbox), inner_vbox, FALSE, FALSE, 0); /* Enable BBDB checkbox */ - check = gtk_check_button_new_with_mnemonic (_("_Automatically create entries in the addressbook when responding to messages")); + check = gtk_check_button_new_with_mnemonic (_("_Auto-create addressbook entries when replying to messages")); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), gconf_client_get_bool (target->gconf, GCONF_KEY_ENABLE, NULL)); g_signal_connect (GTK_TOGGLE_BUTTON (check), "toggled", G_CALLBACK (enable_toggled_cb), stuff); gtk_box_pack_start (GTK_BOX (inner_vbox), check, FALSE, FALSE, 0); @@ -598,7 +600,7 @@ bbdb_page_factory (EPlugin *ep, EConfigHookItemFactoryData *hook_data) gtk_box_pack_start (GTK_BOX (hbox), inner_vbox, FALSE, FALSE, 0); /* Enable Gaim Checkbox */ - check_gaim = gtk_check_button_new_with_mnemonic (_("Periodically synchronize contact information and images from Pidgin buddy list")); + check_gaim = gtk_check_button_new_with_mnemonic (_("Synchronize contact info and images from Pidgin buddy list")); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check_gaim), gconf_client_get_bool (target->gconf, GCONF_KEY_ENABLE_GAIM, NULL)); g_signal_connect (GTK_TOGGLE_BUTTON (check_gaim), "toggled", G_CALLBACK (enable_gaim_toggled_cb), stuff); gtk_box_pack_start (GTK_BOX (inner_vbox), check_gaim, FALSE, FALSE, 0); diff --git a/plugins/caldav/ChangeLog b/plugins/caldav/ChangeLog index 0801aed8cd..43cc26248f 100644 --- a/plugins/caldav/ChangeLog +++ b/plugins/caldav/ChangeLog @@ -1,3 +1,20 @@ +2008-08-07 Paul Bolle <pebolle@tiscali.nl> + + ** Fixes bug #546785 + + * caldav-source.c: (e_plugin_lib_enable): The caldav plugin is noisy + when enabled: + "CalDAV Eplugin starting up ..." + Make this plugin just as polite as google-account-setup. + +2008-07-31 Matthew Barnes <mbarnes@redhat.com> + + ** Fixes part of bug #545568 + + * org-gnome-caldav.eplug.xml: + Add "system_plugin=true" so it's not shown in the Plugin Manager. + This plugin is not designed to be disabled by the user. + 2007-10-26 Kjartan Maraas <kmaraas@gnome.org> * caldav-source.c: (ensure_caldav_source_group): diff --git a/plugins/caldav/caldav-source.c b/plugins/caldav/caldav-source.c index 784e79bf8d..8b48bf6319 100644 --- a/plugins/caldav/caldav-source.c +++ b/plugins/caldav/caldav-source.c @@ -38,6 +38,8 @@ #include <string.h> +#define d(x) + /*****************************************************************************/ /* prototypes */ int e_plugin_lib_enable (EPluginLib *ep, @@ -84,7 +86,7 @@ e_plugin_lib_enable (EPluginLib *ep, int enable) { if (enable) { - g_print ("CalDAV Eplugin starting up ...\n"); + d(g_print ("CalDAV Eplugin starting up ...\n")); ensure_caldav_source_group (); } diff --git a/plugins/caldav/org-gnome-evolution-caldav.eplug.xml b/plugins/caldav/org-gnome-evolution-caldav.eplug.xml index 40710b39f7..dc728f1e7f 100644 --- a/plugins/caldav/org-gnome-evolution-caldav.eplug.xml +++ b/plugins/caldav/org-gnome-evolution-caldav.eplug.xml @@ -7,7 +7,8 @@ load-on-startup="true" domain="@GETTEXT_PACKAGE@" localedir="@LOCALEDIR@" - _name="CalDAV sources"> + _name="CalDAV sources" + system_plugin="true"> <author name="Christian Kellner" email="gicmo@gnome.org"/> <_description>CalDAV Calendar sources</_description> diff --git a/plugins/calendar-file/ChangeLog b/plugins/calendar-file/ChangeLog index a98c581934..351dc79c96 100644 --- a/plugins/calendar-file/ChangeLog +++ b/plugins/calendar-file/ChangeLog @@ -1,3 +1,11 @@ +2008-07-31 Matthew Barnes <mbarnes@redhat.com> + + ** Fixes part of bug #545568 + + * org-gnome-calendar-file.eplug.xml: + Add "system_plugin=true" so it's not shown in the Plugin Manager. + This plugin is not designed to be disabled by the user. + 2007-03-20 Matthew Barnes <mbarnes@redhat.com> ** Fixes part of bug #419524 diff --git a/plugins/calendar-file/org-gnome-calendar-file.eplug.xml b/plugins/calendar-file/org-gnome-calendar-file.eplug.xml index f2af9a0037..246b176059 100644 --- a/plugins/calendar-file/org-gnome-calendar-file.eplug.xml +++ b/plugins/calendar-file/org-gnome-calendar-file.eplug.xml @@ -4,7 +4,8 @@ type="shlib" location="@PLUGINDIR@/liborg-gnome-calendar-file@SOEXT@" id="org.gnome.evolution.calendar.file" - _name="Local Calendars"> + _name="Local Calendars" + system_plugin="true"> <author name="JP Rosevear" email="jpr@novell.com"/> <_description>Provides core functionality for local calendars.</_description> <hook class="org.gnome.evolution.calendar.config:1.0"> diff --git a/plugins/calendar-http/ChangeLog b/plugins/calendar-http/ChangeLog index 76fb7d8cba..8cadbba783 100644 --- a/plugins/calendar-http/ChangeLog +++ b/plugins/calendar-http/ChangeLog @@ -1,3 +1,11 @@ +2008-07-31 Matthew Barnes <mbarnes@redhat.com> + + ** Fixes part of bug #545568 + + * org-gnome-calendar-http.eplug.xml: + Add "system_plugin=true" so it's not shown in the Plugin Manager. + This plugin is not designed to be disabled by the user. + 2008-03-31 Suman Manjunath <msuman@novell.com> ** Fix for bug #346555 diff --git a/plugins/calendar-http/org-gnome-calendar-http.eplug.xml b/plugins/calendar-http/org-gnome-calendar-http.eplug.xml index 3d4f885aec..8b75a0f2c3 100644 --- a/plugins/calendar-http/org-gnome-calendar-http.eplug.xml +++ b/plugins/calendar-http/org-gnome-calendar-http.eplug.xml @@ -4,7 +4,8 @@ type="shlib" location="@PLUGINDIR@/liborg-gnome-calendar-http@SOEXT@" id="org.gnome.evolution.calendar.http" - _name="HTTP Calendars"> + _name="HTTP Calendars" + system_plugin="true"> <author name="David Trowbridge" email="David.Trowbridge@Colorado.edu"/> <_description>Provides core functionality for webcal and http calendars.</_description> <hook class="org.gnome.evolution.calendar.config:1.0"> diff --git a/plugins/calendar-weather/ChangeLog b/plugins/calendar-weather/ChangeLog index 78f2dc28d7..13f1f22a89 100644 --- a/plugins/calendar-weather/ChangeLog +++ b/plugins/calendar-weather/ChangeLog @@ -1,3 +1,11 @@ +2008-07-31 Matthew Barnes <mbarnes@redhat.com> + + ** Fixes part of bug #545568 + + * org-gnome-calendar-weather.eplug.xml: + Add "system_plugin=true" so it's not shown in the Plugin Manager. + This plugin is not designed to be disabled by the user. + 2008-03-31 Suman Manjunath <msuman@novell.com> ** Fix for bug #346555 diff --git a/plugins/calendar-weather/org-gnome-calendar-weather.eplug.xml b/plugins/calendar-weather/org-gnome-calendar-weather.eplug.xml index 442fd83074..e6767d128a 100644 --- a/plugins/calendar-weather/org-gnome-calendar-weather.eplug.xml +++ b/plugins/calendar-weather/org-gnome-calendar-weather.eplug.xml @@ -4,7 +4,8 @@ type="shlib" location="@PLUGINDIR@/liborg-gnome-calendar-weather@SOEXT@" id="org.gnome.evolution.calendar.weather" - _name="Weather Calendars"> + _name="Weather Calendars" + system_plugin="true"> <_description>Provides core functionality for weather calendars.</_description> <author name="David Trowbridge" email="trowbrds@cs.colorado.edu"/> <hook class="org.gnome.evolution.calendar.config:1.0"> diff --git a/plugins/email-custom-header/ChangeLog b/plugins/email-custom-header/ChangeLog index 0fbc6ac296..7f4dc1918a 100644 --- a/plugins/email-custom-header/ChangeLog +++ b/plugins/email-custom-header/ChangeLog @@ -1,3 +1,31 @@ +2008-08-01 Matthew Barnes <mbarnes@redhat.com> + + ** Fixes bug #543755 + + * apps_evolution_email_custom_header.schemas.in: Fix typo. + +2008-07-22 Milan Crha <mcrha@redhat.com> + + ** Part of fix for bug #544022 + + * email-custom-header.c: Compiler warning cleanup. + +2008-06-25 Ashish Shrivastava <shashish@novell.com> + * email-custom-header.glade: Marked strings for + translation. + remove "window1" string from translation. + +2008-06-24 Ashish Shrivastava <shashish@novell.com> + + * email-custom-header.c: (e_plugin_lib_get_configure_widget), + (org_gnome_email_custom_header_config_option): + Added Support for configuring gconf values. + * email-custom-header.glade: + Configure email-custom-header plugin within the plugin-manager. + * org-gnome-email-custom-header.eplug.xml: Add hook class + for Configure tab. + * Makefile.am: Add new file.(email-custom-header.glade). + 2008-06-02 Matthew Barnes <mbarnes@redhat.com> * Makefile.am: diff --git a/plugins/email-custom-header/Makefile.am b/plugins/email-custom-header/Makefile.am index f7a9c84e2f..91a3b52df4 100644 --- a/plugins/email-custom-header/Makefile.am +++ b/plugins/email-custom-header/Makefile.am @@ -42,7 +42,8 @@ install-data-local: fi glade_DATA = \ - org-gnome-email-custom-header.glade + org-gnome-email-custom-header.glade \ + email-custom-header.glade EXTRA_DIST = \ org-gnome-email-custom-header.eplug.xml \ diff --git a/plugins/email-custom-header/apps_evolution_email_custom_header.schemas.in b/plugins/email-custom-header/apps_evolution_email_custom_header.schemas.in index 79c8a9d8ec..cb3bd97ce8 100644 --- a/plugins/email-custom-header/apps_evolution_email_custom_header.schemas.in +++ b/plugins/email-custom-header/apps_evolution_email_custom_header.schemas.in @@ -8,7 +8,7 @@ <list_type>string</list_type> <default>[Security=Personal;Unclassified;Protected;InConfidence;Secret;Topsecret]</default> <locale name="C"> - <short>List of Custom Header</short> + <short>List of Custom Headers</short> <long>The key specifies the list of custom headers that you can add to an outgoing message. The format for specifying a Header and Header value is: Name of the custom header followed by "=" and the values separated by ";"</long> </locale> </schema> diff --git a/plugins/email-custom-header/email-custom-header.c b/plugins/email-custom-header/email-custom-header.c index ef2ac976a7..6918a9b43c 100644 --- a/plugins/email-custom-header/email-custom-header.c +++ b/plugins/email-custom-header/email-custom-header.c @@ -33,10 +33,28 @@ #include "mail/em-event.h" #include "composer/e-msg-composer.h" #include "libedataserver/e-account.h" +#include "e-util/e-config.h" #include "email-custom-header.h" #define d(x) x +#define GCONF_KEY_CUSTOM_HEADER "/apps/evolution/eplugin/email_custom_header/customHeader" + +typedef struct { + GladeXML *xml; + GConfClient *gconf; + GtkWidget *treeview; + GtkWidget *header_add; + GtkWidget *header_edit; + GtkWidget *header_remove; + GtkListStore *store; +} ConfigData; + +enum { + HEADER_KEY_COLUMN, + HEADER_VALUE_COLUMN, + HEADER_N_COLUMNS, +}; struct _EmailCustomHeaderOptionsDialogPrivate { /* Glade XML data */ @@ -60,8 +78,17 @@ static void epech_dialog_init (GObject *object); static void epech_dialog_dispose (GObject *object); static void epech_setup_widgets (CustomHeaderOptionsDialog *mch); static gint epech_check_existing_composer_window(gconstpointer a, gconstpointer b); - +static void commit_changes (ConfigData *cd); +int e_plugin_lib_enable (EPluginLib *ep, int enable); +GtkWidget *e_plugin_lib_get_configure_widget (EPlugin *epl); gboolean e_plugin_ui_init(GtkUIManager *manager, EMsgComposer *composer); +GtkWidget *org_gnome_email_custom_header_config_option (struct _EPlugin *epl, struct _EConfigHookItemFactoryData *data); + +int +e_plugin_lib_enable (EPluginLib *ep, int enable) +{ + return 0; +} static void epech_get_widgets_data (CustomHeaderOptionsDialog *mch) @@ -242,7 +269,7 @@ epech_get_header_list (CustomHeaderOptionsDialog *mch) client = gconf_client_get_default (); g_return_if_fail (GCONF_IS_CLIENT (client)); - gconf_client_add_dir (client, "/apps/evolution/eplugin/email_custom_header" , GCONF_CLIENT_PRELOAD_ONELEVEL, NULL); + gconf_client_add_dir (client, GCONF_KEY_CUSTOM_HEADER, GCONF_CLIENT_PRELOAD_ONELEVEL, NULL); epech_load_from_gconf (client, "/apps/evolution/eplugin/email_custom_header/customHeader", mch); return; @@ -554,3 +581,388 @@ e_plugin_ui_init (GtkUIManager *manager, return TRUE; } +static void +commit_changes (ConfigData *cd) +{ + GtkTreeModel *model = NULL; + GSList *header_config_list = NULL; + GtkTreeIter iter; + gboolean valid; + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (cd->treeview)); + valid = gtk_tree_model_get_iter_first (model, &iter); + + while (valid) { + gchar *keyword = NULL, *value = NULL; + gtk_tree_model_get (model, &iter, HEADER_KEY_COLUMN, &keyword, -1); + /* Check if the keyword is not empty */ + gtk_tree_model_get (model, &iter, HEADER_VALUE_COLUMN, &value, -1); + /* Check if the keyword is not empty */ + if ((keyword) && (g_utf8_strlen(g_strstrip(keyword), -1) > 0)){ + if ((value) && (g_utf8_strlen(g_strstrip(value), -1) > 0)){ + keyword = g_strconcat (keyword, "=", value, NULL); + } + header_config_list = g_slist_append (header_config_list, g_strdup(keyword)); + } + g_free (keyword); + valid = gtk_tree_model_iter_next (model, &iter); + } + + gconf_client_set_list (cd->gconf, GCONF_KEY_CUSTOM_HEADER, GCONF_VALUE_STRING, header_config_list, NULL); + + g_slist_foreach (header_config_list, (GFunc) g_free, NULL); + g_slist_free (header_config_list); +} + +static void +header_isempty (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, ConfigData *cd) +{ + GtkTreeSelection *selection; + char *keyword = NULL; + char *value = NULL; + gboolean valid; + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (cd->treeview)); + /* move to the previous node */ + valid = gtk_tree_path_prev (path); + + gtk_tree_model_get (model, iter, HEADER_KEY_COLUMN, &keyword, -1); + + if ((keyword) && !(g_utf8_strlen (g_strstrip (keyword), -1) > 0)) + gtk_list_store_remove (cd->store, iter); + + /* Check if we have a valid row to select. If not, then select + * the previous row */ + if (gtk_list_store_iter_is_valid (GTK_LIST_STORE (model), iter)) { + gtk_tree_selection_select_iter (selection, iter); + } else { + if (path && valid) { + gtk_tree_model_get_iter (model, iter, path); + gtk_tree_selection_select_iter (selection, iter); + } + } + + gtk_widget_grab_focus (cd->treeview); + g_free (keyword); + g_free (value); +} + +static gboolean +header_foreach_check_isempty (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, ConfigData *cd) +{ + gboolean valid; + + valid = gtk_tree_model_get_iter_first (model, iter); + while (valid && gtk_list_store_iter_is_valid (cd->store, iter)) { + char *keyword = NULL; + gtk_tree_model_get (model, iter, HEADER_KEY_COLUMN, &keyword, -1); + /* Check if the keyword is not empty and then emit the row-changed + signal (if we delete the row, then the iter gets corrupted) */ + if ((keyword) && !(g_utf8_strlen (g_strstrip (keyword), -1) > 0)) + gtk_tree_model_row_changed (model, path, iter); + + char *value = NULL; + gtk_tree_model_get (model, iter, HEADER_VALUE_COLUMN, &value, -1); + /* Check if the keyword is not empty and then emit the row-changed + signal (if we delete the row, then the iter gets corrupted) */ + if ((value) && !(g_utf8_strlen (g_strstrip (value), -1) > 0)) + gtk_tree_model_row_changed (model, path, iter); + + g_free (keyword); + g_free (value); + + valid = gtk_tree_model_iter_next (model, iter); + } + + return FALSE; +} + +static void +cell_edited_callback (GtkCellRendererText *cell, + gchar *path_string, + gchar *new_text, + ConfigData *cd) +{ + GtkTreeModel *model; + GtkTreeIter iter; + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (cd->treeview)); + + gtk_tree_model_get_iter_from_string (model, &iter, path_string); + + gtk_list_store_set (GTK_LIST_STORE (model), &iter, + HEADER_KEY_COLUMN, new_text, -1); + + commit_changes (cd); +} + +static void +cell_value_edited_callback (GtkCellRendererText *cell, + gchar *path_string, + gchar *new_text, + ConfigData *cd) +{ + GtkTreeModel *model; + GtkTreeIter iter; + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (cd->treeview)); + + gtk_tree_model_get_iter_from_string (model, &iter, path_string); + + gtk_list_store_set (GTK_LIST_STORE (model), &iter, + HEADER_VALUE_COLUMN, new_text, -1); + + commit_changes (cd); +} + +static void +header_add_clicked (GtkButton *button, ConfigData *cd) +{ + GtkTreeModel *model; + GtkTreeIter iter; + gchar *new_header = NULL; + GtkTreeViewColumn *focus_col; + GtkTreePath *path; + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (cd->treeview)); + gtk_tree_model_foreach (model, (GtkTreeModelForeachFunc) header_foreach_check_isempty, cd); + + /* Disconnect from signal so that we can create an empty row */ + g_signal_handlers_disconnect_matched(G_OBJECT(model), G_SIGNAL_MATCH_FUNC, 0, 0, NULL, header_isempty, cd); + + new_header = g_strdup (""); + gtk_list_store_append (GTK_LIST_STORE (model), &iter); + gtk_list_store_set (GTK_LIST_STORE (model), &iter, + HEADER_KEY_COLUMN, new_header, -1); + gtk_list_store_set (GTK_LIST_STORE (model), &iter, + HEADER_VALUE_COLUMN, new_header, -1); + + focus_col = gtk_tree_view_get_column (GTK_TREE_VIEW (cd->treeview), HEADER_KEY_COLUMN); + path = gtk_tree_model_get_path (model, &iter); + + if (path) { + gtk_tree_view_set_cursor (GTK_TREE_VIEW (cd->treeview), path, focus_col, TRUE); + gtk_tree_view_row_activated(GTK_TREE_VIEW(cd->treeview), path, focus_col); + gtk_tree_path_free (path); + } + + /* We have done our job, connect back to the signal */ + g_signal_connect(G_OBJECT(model), "row-changed", G_CALLBACK(header_isempty), cd); +} + +static void +header_remove_clicked (GtkButton *button, ConfigData *cd) +{ + GtkTreeSelection *selection; + GtkTreeModel *model; + GtkTreeIter iter; + GtkTreePath *path; + gboolean valid; + gint len; + + valid = FALSE; + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (cd->treeview)); + if (!gtk_tree_selection_get_selected (selection, &model, &iter)) + return; + + /* Get the path and move to the previous node :) */ + path = gtk_tree_model_get_path (model, &iter); + if (path) + valid = gtk_tree_path_prev(path); + + gtk_list_store_remove (GTK_LIST_STORE (model), &iter); + + len = gtk_tree_model_iter_n_children (model, NULL); + if (len > 0) { + if (gtk_list_store_iter_is_valid (GTK_LIST_STORE(model), &iter)) { + gtk_tree_selection_select_iter (selection, &iter); + } else { + if (path && valid) { + gtk_tree_model_get_iter(model, &iter, path); + gtk_tree_selection_select_iter (selection, &iter); + } + } + } else { + gtk_widget_set_sensitive (cd->header_edit, FALSE); + gtk_widget_set_sensitive (cd->header_remove, FALSE); + } + + gtk_widget_grab_focus(cd->treeview); + gtk_tree_path_free (path); + + commit_changes (cd); +} + +static void +header_edit_clicked (GtkButton *button, ConfigData *cd) +{ + GtkTreeSelection *selection; + GtkTreeModel *model; + GtkTreePath *path; + GtkTreeIter iter; + GtkTreeViewColumn *focus_col; + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (cd->treeview)); + if (!gtk_tree_selection_get_selected (selection, &model, &iter)) + return; + + focus_col = gtk_tree_view_get_column (GTK_TREE_VIEW (cd->treeview), HEADER_KEY_COLUMN); + path = gtk_tree_model_get_path (model, &iter); + + if (path) { + gtk_tree_view_set_cursor (GTK_TREE_VIEW (cd->treeview), path, focus_col, TRUE); + gtk_tree_path_free (path); + } +} + +static void +selection_changed (GtkTreeSelection *selection, ConfigData *cd) +{ + GtkTreeModel *model; + GtkTreeIter iter; + + if (gtk_tree_selection_get_selected (selection, &model, &iter)) { + gtk_widget_set_sensitive (cd->header_edit, TRUE); + gtk_widget_set_sensitive (cd->header_remove, TRUE); + } else { + gtk_widget_set_sensitive (cd->header_edit, FALSE); + gtk_widget_set_sensitive (cd->header_remove, FALSE); + } +} + +static void +destroy_cd_data (gpointer data) +{ + ConfigData *cd = (ConfigData *) data; + + if (!cd) + return; + + g_object_unref (cd->xml); + g_object_unref (cd->gconf); + g_free (cd); +} + +GtkWidget * +e_plugin_lib_get_configure_widget (EPlugin *epl) +{ + GtkCellRenderer *renderer; + GtkTreeSelection *selection; + GtkTreeIter iter; + GtkWidget *hbox; + GSList *list; + GSList *header_list = NULL; + GtkTreeModel *model; + gint index; + gchar *buffer; + char *str_colon = NULL, *str1_colon = NULL; + GtkTreeViewColumn *col; + int col_pos; + + GConfClient *client = gconf_client_get_default(); + + ConfigData *cd = g_new0 (ConfigData, 1); + + char *gladefile; + + gladefile = g_build_filename (EVOLUTION_GLADEDIR, + "email-custom-header.glade", + NULL); + cd->xml = glade_xml_new (gladefile, "ech_configuration_box", NULL); + g_free (gladefile); + + cd->gconf = gconf_client_get_default (); + + cd->treeview = glade_xml_get_widget (cd->xml, "header_treeview"); + + cd->store = gtk_list_store_new (HEADER_N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING); + + gtk_tree_view_set_model (GTK_TREE_VIEW (cd->treeview), GTK_TREE_MODEL (cd->store)); + + renderer = gtk_cell_renderer_text_new (); + gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (cd->treeview), -1, _("Key"), + renderer, "text", HEADER_KEY_COLUMN, NULL); + col = gtk_tree_view_get_column (GTK_TREE_VIEW (cd->treeview), col_pos -1); + gtk_tree_view_column_set_resizable (col, TRUE); + gtk_tree_view_column_set_reorderable(col, TRUE); + g_object_set (col, "min-width", 50, NULL); + + g_object_set (G_OBJECT (renderer), "editable", TRUE, NULL); + g_signal_connect(renderer, "edited", (GCallback) cell_edited_callback, cd); + + renderer = gtk_cell_renderer_text_new (); + col_pos = gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (cd->treeview), -1, _("Values"), + renderer, "text", HEADER_VALUE_COLUMN, NULL); + col = gtk_tree_view_get_column (GTK_TREE_VIEW (cd->treeview), col_pos -1); + gtk_tree_view_column_set_resizable (col, TRUE); + gtk_tree_view_column_set_reorderable(col, TRUE); + g_object_set (G_OBJECT (renderer), "editable", TRUE, NULL); + + g_signal_connect(renderer, "edited", (GCallback) cell_value_edited_callback, cd); + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (cd->treeview)); + gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE); + g_signal_connect (G_OBJECT (selection), "changed", G_CALLBACK (selection_changed), cd); + gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (cd->treeview), TRUE); + + cd->header_add = glade_xml_get_widget (cd->xml, "header_add"); + g_signal_connect (G_OBJECT (cd->header_add), "clicked", G_CALLBACK (header_add_clicked), cd); + + cd->header_remove = glade_xml_get_widget (cd->xml, "header_remove"); + g_signal_connect (G_OBJECT (cd->header_remove), "clicked", G_CALLBACK (header_remove_clicked), cd); + gtk_widget_set_sensitive (cd->header_remove, FALSE); + + cd->header_edit = glade_xml_get_widget (cd->xml, "header_edit"); + g_signal_connect (G_OBJECT (cd->header_edit), "clicked", G_CALLBACK (header_edit_clicked), cd); + gtk_widget_set_sensitive (cd->header_edit, FALSE); + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (cd->treeview)); + g_signal_connect(G_OBJECT(model), "row-changed", G_CALLBACK(header_isempty), cd); + + /* Populate tree view with values from gconf */ + header_list = gconf_client_get_list (client,GCONF_KEY_CUSTOM_HEADER,GCONF_VALUE_STRING, NULL); + + for (list = header_list; list; list = g_slist_next (list)) { + gtk_list_store_append (cd->store, &iter); + gchar **parse_header_list; + buffer = list->data; + parse_header_list = g_strsplit_set (buffer, "=,", -1); + str_colon = g_strconcat (parse_header_list[0], "", NULL); + gtk_list_store_set (cd->store, &iter, HEADER_KEY_COLUMN, str_colon, -1); + + for (index = 0; parse_header_list[index+1] ; ++index) { + str1_colon = g_strconcat (parse_header_list[index+1], "", NULL); + gtk_list_store_set (cd->store, &iter, HEADER_VALUE_COLUMN, str1_colon, -1); + } + } + g_free (str_colon); + g_free (str1_colon); + + if (header_list) { + g_slist_foreach (header_list, (GFunc) g_free, NULL); + g_slist_free (header_list); + } + + /* Add the list here */ + + hbox = gtk_vbox_new (FALSE, 0); + + gtk_box_pack_start (GTK_BOX (hbox), glade_xml_get_widget (cd->xml, "ech_configuration_box"), TRUE, TRUE, 0); + + /* to let free data properly on destroy of configuration widget */ + g_object_set_data_full (G_OBJECT (hbox), "mycd-data", cd, destroy_cd_data); + + return hbox; +} + +/* Configuration in Mail Prefs Page goes here */ + +GtkWidget * +org_gnome_email_custom_header_config_option (struct _EPlugin *epl, struct _EConfigHookItemFactoryData *data) +{ + /* This function and the hook needs to be removed, + once the configure code is thoroughly tested */ + + return NULL; + +} diff --git a/plugins/email-custom-header/email-custom-header.glade b/plugins/email-custom-header/email-custom-header.glade new file mode 100644 index 0000000000..14a618f977 --- /dev/null +++ b/plugins/email-custom-header/email-custom-header.glade @@ -0,0 +1,176 @@ +<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*--> +<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd"> + +<glade-interface> + +<widget class="GtkWindow" id="window1"> + <property name="visible">True</property> + <property name="title" translatable="no">window1</property> + <property name="type">GTK_WINDOW_TOPLEVEL</property> + <property name="window_position">GTK_WIN_POS_NONE</property> + <property name="modal">False</property> + <property name="resizable">True</property> + <property name="destroy_with_parent">False</property> + <property name="decorated">True</property> + <property name="skip_taskbar_hint">False</property> + <property name="skip_pager_hint">False</property> + <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property> + <property name="gravity">GDK_GRAVITY_NORTH_WEST</property> + <property name="focus_on_map">True</property> + <property name="urgency_hint">False</property> + + <child> + <widget class="GtkVBox" id="ech_configuration_box"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkVBox" id="vbox2"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkLabel" id="label1"> + <property name="visible">True</property> + <property name="label" translatable="yes" comments="To translators: This string is used while adding a new message header to configuration, to specifying the format of the key values">The format for specifying a Custom Header key value is: +Name of the Custom Header key values separated by ";".</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_CENTER</property> + <property name="wrap">True</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkVBox" id="header_configuration_box"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">5</property> + + <child> + <widget class="GtkHBox" id="header_container"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">6</property> + + <child> + <widget class="GtkScrolledWindow" id="scrolledwindow1"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="shadow_type">GTK_SHADOW_NONE</property> + <property name="window_placement">GTK_CORNER_TOP_LEFT</property> + + <child> + <widget class="GtkTreeView" id="header_treeview"> + <property name="border_width">1</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="headers_visible">True</property> + <property name="rules_hint">False</property> + <property name="reorderable">False</property> + <property name="enable_search">True</property> + <property name="fixed_height_mode">False</property> + <property name="hover_selection">False</property> + <property name="hover_expand">False</property> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkVButtonBox" id="vbuttonbox1"> + <property name="visible">True</property> + <property name="layout_style">GTK_BUTTONBOX_START</property> + <property name="spacing">6</property> + + <child> + <widget class="GtkButton" id="header_add"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-add</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + </widget> + </child> + + <child> + <widget class="GtkButton" id="header_edit"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-edit</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + </widget> + </child> + + <child> + <widget class="GtkButton" id="header_remove"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-remove</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + </child> +</widget> + +</glade-interface> diff --git a/plugins/email-custom-header/org-gnome-email-custom-header.eplug.xml b/plugins/email-custom-header/org-gnome-email-custom-header.eplug.xml index 055acc1030..94102ba62a 100644 --- a/plugins/email-custom-header/org-gnome-email-custom-header.eplug.xml +++ b/plugins/email-custom-header/org-gnome-email-custom-header.eplug.xml @@ -18,5 +18,12 @@ </menubar> </ui-manager> </hook> + + <hook class="org.gnome.evolution.mail.config:1.0"> + <group target="prefs" id="org.gnome.evolution.mail.composerPrefs"> + <item type="section" path="00.general/10.alerts/" factory="org_gnome_email_custom_header_config_option"/> + </group> + </hook> + </e-plugin> </e-plugin-list> diff --git a/plugins/exchange-operations/ChangeLog b/plugins/exchange-operations/ChangeLog index 5386d2bbfd..f16f1301cf 100644 --- a/plugins/exchange-operations/ChangeLog +++ b/plugins/exchange-operations/ChangeLog @@ -1,3 +1,34 @@ +2008-08-06 Milan Crha <mcrha@redhat.com> + + ** Fix for bug #435969 + + * org-gnome-exchange-operations.eplug.xml: + Show authentication section in an account druid too. + +2008-07-31 Matthew Barnes <mbarnes@redhat.com> + + ** Fixes part of bug #545568 + + * org-gnome-exchange-operations.eplug.xml: + Add "system_plugin=true" so it's not shown in the Plugin Manager. + This plugin is not designed to be disabled by the user. + +2008-07-30 Milan Crha <mcrha@redhat.com> + + ** Part of fix for bug #500389 + + * exchange-account-setup.c: (gal_auth_to_string), + (owa_authenticate_user): Use default authentication for GAL. + +2008-07-11 Bharath Acharya <abharath@novell.com> + + ** Fix for bug #542149 + + * exchange-folder-subscription.c: (subscribe_to_folder): Display an + error message to restart if user tries to subscribe to other's mailbox. + * org-gnome-exchange-operations.error.xml: Added the corresponding + error message. + 2008-06-16 Milan Crha <mcrha@redhat.com> ** Fix for bug #273627 diff --git a/plugins/exchange-operations/exchange-account-setup.c b/plugins/exchange-operations/exchange-account-setup.c index 55d3d7c8a1..3e9e64ea6a 100644 --- a/plugins/exchange-operations/exchange-account-setup.c +++ b/plugins/exchange-operations/exchange-account-setup.c @@ -413,6 +413,18 @@ print_error (const char *owa_url, E2kAutoconfigResult result) } } +static const char * +gal_auth_to_string (E2kAutoconfigGalAuthPref ad_auth) +{ + switch (ad_auth) { + case E2K_AUTOCONFIG_USE_GAL_DEFAULT: return "default"; + case E2K_AUTOCONFIG_USE_GAL_BASIC: return "basic"; + case E2K_AUTOCONFIG_USE_GAL_NTLM: return "ntlm"; + } + + return "default"; +} + static void owa_authenticate_user(GtkWidget *button, EConfig *config) { @@ -430,6 +442,7 @@ owa_authenticate_user(GtkWidget *button, EConfig *config) exchange_params = g_new0 (ExchangeParams, 1); exchange_params->host = NULL; exchange_params->ad_server = NULL; + exchange_params->ad_auth = E2K_AUTOCONFIG_USE_GAL_DEFAULT; exchange_params->mailbox = NULL; exchange_params->owa_path = NULL; exchange_params->is_ntlm = TRUE; @@ -487,6 +500,7 @@ owa_authenticate_user(GtkWidget *button, EConfig *config) camel_url_set_param (url, "ad_server", valid ? exchange_params->ad_server: NULL); camel_url_set_param (url, "mailbox", valid ? exchange_params->mailbox : NULL); camel_url_set_param (url, "owa_path", valid ? exchange_params->owa_path : NULL); + camel_url_set_param (url, "ad_auth", valid ? gal_auth_to_string (exchange_params->ad_auth) : NULL); if (mailbox_entry) { const char *par = camel_url_get_param (url, "mailbox"); diff --git a/plugins/exchange-operations/exchange-folder-subscription.c b/plugins/exchange-operations/exchange-folder-subscription.c index 021a06434b..9706dd29ab 100644 --- a/plugins/exchange-operations/exchange-folder-subscription.c +++ b/plugins/exchange-operations/exchange-folder-subscription.c @@ -237,9 +237,12 @@ subscribe_to_folder (GtkWidget *dialog, gint response, gpointer data) user_email_address, folder_name, &folder); g_free (folder_name); + gtk_widget_hide (dialog); switch (result) { case EXCHANGE_ACCOUNT_FOLDER_OK: exchange_account_rescan_tree (subscription_info->account); + if (!g_ascii_strcasecmp (e_folder_get_type_string (folder), "mail")) + e_error_run (NULL, ERROR_DOMAIN ":folder-restart-evo", NULL); break; case EXCHANGE_ACCOUNT_FOLDER_ALREADY_EXISTS: e_error_run (NULL, ERROR_DOMAIN ":folder-exists-error", NULL); diff --git a/plugins/exchange-operations/org-gnome-exchange-operations.eplug.xml b/plugins/exchange-operations/org-gnome-exchange-operations.eplug.xml index b551b8d746..16f4a7b311 100644 --- a/plugins/exchange-operations/org-gnome-exchange-operations.eplug.xml +++ b/plugins/exchange-operations/org-gnome-exchange-operations.eplug.xml @@ -5,7 +5,8 @@ id="org.gnome.evolution.plugin.exchange-operations" location="@PLUGINDIR@/liborg-gnome-exchange-operations@SOEXT@" load-on-startup="true" - _name="Exchange Operations"> + _name="Exchange Operations" + system_plugin="true"> <author name="Sushma Rai" email="rsushma@novell.com"/> <author name="Praveen Kumar" email="kpraveen@novell.com"/> <author name="Shakti Sen" email="shprasad@novell.com"/> @@ -37,6 +38,9 @@ <item type="item_table" path="10.receive/10.config/20.owa" factory="org_gnome_exchange_owa_url"/> + <item type="section" + path="10.receive/30.auth/00.exchange_auth" + factory="org_gnome_exchange_auth_section"/> </group> <group diff --git a/plugins/exchange-operations/org-gnome-exchange-operations.error.xml b/plugins/exchange-operations/org-gnome-exchange-operations.error.xml index 0aa4b6fb79..c162f365ec 100644 --- a/plugins/exchange-operations/org-gnome-exchange-operations.error.xml +++ b/plugins/exchange-operations/org-gnome-exchange-operations.error.xml @@ -221,6 +221,10 @@ username, and password, and try again.</_secondary> <_primary>Folder already exists</_primary> </error> + <error id="folder-restart-evo" type="error"> + <_primary>Evolution requires a restart to load the subscribed user's mailbox</_primary> + </error> + <error id="folder-doesnt-exist-error" type="error"> <_primary>Folder does not exist</_primary> </error> diff --git a/plugins/external-editor/ChangeLog b/plugins/external-editor/ChangeLog index d49ecb97a4..1eb9b8fdef 100644 --- a/plugins/external-editor/ChangeLog +++ b/plugins/external-editor/ChangeLog @@ -1,3 +1,10 @@ +2008-08-01 Matthew Barnes <mbarnes@redhat.com> + + ** Fixes bug #517151 + + * org-gnome-external-editor.xml: + Give a mnemonic to "Composer in External Editor". + 2008-04-17 Sankar P <psankar@novell.com> * Makefile.am: diff --git a/plugins/external-editor/org-gnome-external-editor.xml b/plugins/external-editor/org-gnome-external-editor.xml index 71be9e1266..e472f8bd1c 100644 --- a/plugins/external-editor/org-gnome-external-editor.xml +++ b/plugins/external-editor/org-gnome-external-editor.xml @@ -1,7 +1,7 @@ <Root> <commands> - <cmd name="EPExtEditor" _label="Compose in External Editor" + <cmd name="EPExtEditor" _label="Compose in _External Editor" _tip="Compose messages using an external editor" pixtype="pixmap"/> diff --git a/plugins/google-account-setup/ChangeLog b/plugins/google-account-setup/ChangeLog index a1019c05e7..037aa01a3c 100644 --- a/plugins/google-account-setup/ChangeLog +++ b/plugins/google-account-setup/ChangeLog @@ -1,3 +1,41 @@ +2008-08-07 Milan Crha <mcrha@redhat.com> + + ** Fix for bug #535745 + + * google-source.c: (sanitize_user_mail), (construct_default_uri), + (is_default_uri), (init_combo_values), (user_changed), + (cal_combo_changed), (claim_error), (retrieve_list_clicked), + (plugin_google): New widgets and functionality to retrieve list + of subscribed calendars from the Google account and let user choose + which one would be shown. + +2008-08-01 Matthew Barnes <mbarnes@redhat.com> + + ** Fixes bug #544860 + + * google-contacts-source.c (plugin_google_contacts): + Add translator comments for split "update every" sentence. + +2008-07-31 Matthew Barnes <mbarnes@redhat.com> + + ** Fixes part of bug #545568 + + * org-gnome-evolution-google.eplug.xml: + Add "system_plugin=true" so it's not shown in the Plugin Manager. + This plugin is not designed to be disabled by the user. + +2008-05-24 Jörgen Scheibengruber <mfcn@gmx.de> + + * Makefile.am: + * google-contacts-source.c (ensure_google_contacts_source_group), + (remove_google_contacts_source_group), (on_username_entry_changed), + (on_update_cb_toggled), (on_interval_sb_value_changed), + (plugin_google_contacts): + * google-contacts-source.h: + * google-source.c (e_plugin_lib_enable): + * org-gnome-evolution-google.eplug.xml: + Added a UI for the addressbook part + 2008-03-31 Suman Manjunath <msuman@novell.com> ** Fix for bug #346555 diff --git a/plugins/google-account-setup/Makefile.am b/plugins/google-account-setup/Makefile.am index 898083c65a..d1c385b172 100644 --- a/plugins/google-account-setup/Makefile.am +++ b/plugins/google-account-setup/Makefile.am @@ -1,5 +1,6 @@ INCLUDES = \ $(EVOLUTION_CALENDAR_CFLAGS) \ + $(EVOLUTION_ADDRESSBOOK_CFLAGS) \ -I . \ -I$(top_srcdir) \ -DCALDAV_GLADEDIR=\""$(gladedir)"\" @@ -10,7 +11,9 @@ plugin_DATA = org-gnome-evolution-google.eplug plugin_LTLIBRARIES = liborg-gnome-evolution-google.la liborg_gnome_evolution_google_la_SOURCES = \ - google-source.c + google-source.c \ + google-contacts-source.h \ + google-contacts-source.c liborg_gnome_evolution_google_la_LIBADD = \ $(EVOLUTION_CALENDAR_LIBS) \ diff --git a/plugins/google-account-setup/google-contacts-source.c b/plugins/google-account-setup/google-contacts-source.c new file mode 100644 index 0000000000..d0c8e71e63 --- /dev/null +++ b/plugins/google-account-setup/google-contacts-source.c @@ -0,0 +1,269 @@ +/* + * Copyright 2008, Joergen Scheibengruber <joergen.scheibengruber@googlemail.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU Lesser General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include <config.h> +#include <string.h> + +#include <glib/gi18n-lib.h> +#include <glib.h> + +#include <gtk/gtk.h> + +#include <e-util/e-config.h> +#include <e-util/e-plugin.h> +#include <addressbook/gui/widgets/eab-config.h> + +#include <libedataserver/e-source.h> +#include <libedataserver/e-source-list.h> +#include <libedataserver/e-url.h> +#include <libedataserver/e-account-list.h> + +#include "google-contacts-source.h" + + +void +ensure_google_contacts_source_group (void) +{ + ESourceList *source_list; + ESourceGroup *group; + + source_list = e_source_list_new_for_gconf_default ("/apps/evolution/addressbook/sources"); + + if (source_list == NULL) { + return; + } + + group = e_source_list_peek_group_by_name (source_list, _("Google")); + + if (group == NULL) { + gboolean res; + + group = e_source_group_new (_("Google"), "google://"); + res = e_source_list_add_group (source_list, group, -1); + + if (res == FALSE) { + g_warning ("Could not add Google source group!"); + } else { + e_source_list_sync (source_list, NULL); + } + + g_object_unref (group); + } + g_object_unref (source_list); +} + +void +remove_google_contacts_source_group (void) +{ + ESourceList *source_list; + ESourceGroup *group; + + source_list = e_source_list_new_for_gconf_default ("/apps/evolution/addressbook/sources"); + + if (source_list == NULL) { + return; + } + + group = e_source_list_peek_group_by_name (source_list, _("Google")); + + if (group) { + GSList *sources; + + sources = e_source_group_peek_sources (group); + + if (NULL == sources) { + e_source_list_remove_group (source_list, group); + e_source_list_sync (source_list, NULL); + } + } + g_object_unref (source_list); +} + +static void +on_username_entry_changed (GtkEntry *entry, gpointer user_data) +{ + ESource *source = user_data; + const char *text; + char *username; + + text = gtk_entry_get_text (entry); + + if (strstr (text, "@")) { + username = g_strdup (text); + } else { + username = g_strdup_printf ("%s@gmail.com", text); + } + + e_source_set_relative_uri (source, username); + e_source_set_property (source, "username", username); + e_source_set_property (source, "auth", "plain/password"); + g_free (username); +} + +static void +on_update_cb_toggled (GtkToggleButton *tb, gpointer user_data) +{ + ESource *source = user_data; + GtkWidget *sb = g_object_get_data (G_OBJECT (tb), "sb"); + + gtk_widget_set_sensitive (sb, gtk_toggle_button_get_active (tb)); + if (gtk_toggle_button_get_active (tb)) { + gdouble value; + char *value_string; + + value = gtk_spin_button_get_value (GTK_SPIN_BUTTON (sb)); + value_string = g_strdup_printf ("%d", (int)(value * 60.0)); + e_source_set_property (source, "refresh-interval", value_string); + g_free (value_string); + } else { + e_source_set_property (source, "refresh-interval", "-1"); + } +} + +static void +on_interval_sb_value_changed (GtkSpinButton *sb, gpointer user_data) +{ + ESource *source = user_data; + gdouble value; + char *value_string; + + value = gtk_spin_button_get_value (sb); + value_string = g_strdup_printf ("%d", (int)(value * 60.0)); + e_source_set_property (source, "refresh-interval", value_string); + g_free (value_string); +} + +GtkWidget * +plugin_google_contacts (EPlugin *epl, + EConfigHookItemFactoryData *data) +{ + EABConfigTargetSource *t = (EABConfigTargetSource *) data->target; + ESource *source; + ESourceGroup *group; + const char *base_uri; + const char *username; + const char *refresh_interval_str; + int refresh_interval; + GtkWidget *parent; + GtkWidget *vbox; + + GtkWidget *section; + GtkWidget *vbox2; + + GtkWidget *hbox; + GtkWidget *spacer; + GtkWidget *label; + GtkWidget *username_entry; + + GtkWidget *update_cb; + GtkWidget *interval_sb; + + + source = t->source; + group = e_source_peek_group (source); + + base_uri = e_source_group_peek_base_uri (group); + + g_object_set_data_full (G_OBJECT (epl), "widget", NULL, + (GDestroyNotify)gtk_widget_destroy); + + if (strcmp (base_uri, "google://")) { + return NULL; + } + + /* Build up the UI */ + parent = data->parent; + vbox = gtk_widget_get_ancestor (gtk_widget_get_parent (parent), GTK_TYPE_VBOX); + + vbox2 = gtk_vbox_new (FALSE, 6); + gtk_box_pack_start (GTK_BOX (vbox), vbox2, FALSE, FALSE, 0); + + section = gtk_label_new (NULL); + gtk_label_set_markup (GTK_LABEL (section), _("<b>Server</b>")); + gtk_misc_set_alignment (GTK_MISC (section), 0.0, 0.0); + gtk_box_pack_start (GTK_BOX (vbox2), section, FALSE, FALSE, 0); + + hbox = gtk_hbox_new (FALSE, 10); + gtk_box_pack_start (GTK_BOX (vbox2), hbox, TRUE, TRUE, 0); + + spacer = gtk_label_new (" "); + gtk_box_pack_start (GTK_BOX (hbox), spacer, FALSE, FALSE, 0); + + label = gtk_label_new (_("Username:")); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + + username_entry = gtk_entry_new (); + username = e_source_get_property (source, "username"); + if (username) { + gtk_entry_set_text (GTK_ENTRY (username_entry), username); + } + gtk_box_pack_start (GTK_BOX (hbox), username_entry, TRUE, TRUE, 0); + + hbox = gtk_hbox_new (FALSE, 10); + gtk_box_pack_start (GTK_BOX (vbox2), hbox, TRUE, TRUE, 0); + + spacer = gtk_label_new (" "); + gtk_box_pack_start (GTK_BOX (hbox), spacer, FALSE, FALSE, 0); + + refresh_interval_str = e_source_get_property (source, "refresh-interval"); + if (refresh_interval_str && + (1 == sscanf (refresh_interval_str, "%d", &refresh_interval))) { + } else { + refresh_interval = -1; + } + + /* Translators: This is the first half of the sentence "Update + * every NNN minute(s)", where NNN is a spin button widget. */ + update_cb = gtk_check_button_new_with_label (_("Update every")); + gtk_box_pack_start (GTK_BOX (hbox), update_cb, FALSE, FALSE, 0); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (update_cb), + refresh_interval > 0); + + interval_sb = gtk_spin_button_new_with_range (1, 60, 1); + gtk_widget_set_sensitive (interval_sb, + refresh_interval > 0); + gtk_spin_button_set_value (GTK_SPIN_BUTTON (interval_sb), + refresh_interval > 0 ? refresh_interval / 60 : 30); + gtk_box_pack_start (GTK_BOX (hbox), interval_sb, FALSE, FALSE, 0); + + /* Translators: This is the second half of the sentence "Update + * every NNN minute(s)", where NNN is a spin button widget. */ + label = gtk_label_new (_("minute(s)")); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + + gtk_widget_show_all (vbox2); + + g_object_set_data (G_OBJECT (update_cb), "sb", interval_sb); + g_object_set_data_full (G_OBJECT (epl), "widget", vbox2, + (GDestroyNotify)gtk_widget_destroy); + + g_signal_connect (G_OBJECT (username_entry), "changed", + G_CALLBACK (on_username_entry_changed), + source); + g_signal_connect (G_OBJECT (update_cb), "toggled", + G_CALLBACK (on_update_cb_toggled), + source); + g_signal_connect (G_OBJECT (interval_sb), "value-changed", + G_CALLBACK (on_interval_sb_value_changed), + source); + + return NULL; +} + + diff --git a/plugins/google-account-setup/google-contacts-source.h b/plugins/google-account-setup/google-contacts-source.h new file mode 100644 index 0000000000..17421fb232 --- /dev/null +++ b/plugins/google-account-setup/google-contacts-source.h @@ -0,0 +1,30 @@ +/* + * Copyright 2008, Joergen Scheibengruber <joergen.scheibengruber@googlemail.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU Lesser General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef __GOOGLE_CONTACTS_SOURCE_H__ +#define __GOOGLE_CONTACTS_SOURCE_H__ + +GtkWidget *plugin_google_contacts (EPlugin *epl, + EConfigHookItemFactoryData *data); + +void ensure_google_contacts_source_group (void); + +void remove_google_contacts_source_group (void); + +#endif diff --git a/plugins/google-account-setup/google-source.c b/plugins/google-account-setup/google-source.c index d586b3027e..57727c6122 100644 --- a/plugins/google-account-setup/google-source.c +++ b/plugins/google-account-setup/google-source.c @@ -40,8 +40,18 @@ #include <libedataserver/e-url.h> #include <libedataserver/e-account-list.h> #include <libecal/e-cal.h> +#include <libedataserverui/e-cell-renderer-color.h> +#include <libedataserverui/e-passwords.h> + +#include <google/libgdata/gdata-service-iface.h> +#include <google/libgdata/gdata-feed.h> +#include <google/libgdata-google/gdata-google-service.h> + +#include "google-contacts-source.h" #define CALENDAR_LOCATION "http://www.google.com/calendar/feeds/" +#define CALENDAR_DEFAULT_PATH "/private/full" +#define URL_GET_SUBSCRIBED_CALENDARS "http://www.google.com/calendar/feeds/default/allcalendars/full" #define d(x) @@ -56,6 +66,8 @@ GtkWidget * plugin_google (EPlugin *epl, /*****************************************************************************/ /* plugin intialization */ + + static void ensure_google_source_group () { @@ -96,6 +108,9 @@ e_plugin_lib_enable (EPluginLib *ep, int enable) if (enable) { d(printf ("\n Google Eplugin starting up ...\n")); ensure_google_source_group (); + ensure_google_contacts_source_group (); + } else { + remove_google_contacts_source_group (); } return 0; @@ -169,6 +184,83 @@ is_email (const char *address) return TRUE; } +static char * +sanitize_user_mail (const char *user) +{ + if (!user) + return NULL; + + if (!is_email (user)) { + return g_strconcat (user, "%40gmail.com", NULL); + } else { + char *tmp = g_malloc0 (sizeof (char) * (1 + strlen (user) + 2)); + char *at = strchr (user, '@'); + + strncpy (tmp, user, at - user); + strcat (tmp, "%40"); + strcat (tmp, at + 1); + + return tmp; + } +} + +static char * +construct_default_uri (const char *username) +{ + char *user, *uri; + + user = sanitize_user_mail (username); + uri = g_strconcat (CALENDAR_LOCATION, user, CALENDAR_DEFAULT_PATH, NULL); + g_free (user); + + return uri; +} + +/* checks whether the given_uri is pointing to the default user's calendar or not */ +static gboolean +is_default_uri (const char *given_uri, const char *username) +{ + char *uri, *at; + int ats; + gboolean res; + + if (!given_uri) + return TRUE; + + uri = construct_default_uri (username); + + /* count number of '@' in given_uri to know how much memory will be required */ + ats = 0; + for (at = strchr (given_uri, '@'); at; at = strchr (at + 1, '@')) { + ats++; + } + + if (!ats) + res = g_ascii_strcasecmp (given_uri, uri) == 0; + else { + const char *last; + char *tmp = g_malloc0 (sizeof (char) * (1 + strlen (given_uri) + (2 * ats))); + + last = given_uri; + for (at = strchr (last, '@'); at; at = strchr (at + 1, '@')) { + strncat (tmp, last, at - last); + strcat (tmp, "%40"); + last = at + 1; + } + strcat (tmp, last); + + res = g_ascii_strcasecmp (tmp, uri) == 0; + + g_free (tmp); + } + + g_free (uri); + + return res; +} + +static void init_combo_values (GtkComboBox *combo, const char *deftitle, const char *defuri); + static void user_changed (GtkEntry *editable, ESource *source) { @@ -176,10 +268,8 @@ user_changed (GtkEntry *editable, ESource *source) char *uri; char *ruri; const char *user; - char *projection; uri = e_source_get_uri (source); - user = gtk_entry_get_text (GTK_ENTRY (editable)); if (uri == NULL) { g_free (uri); @@ -188,24 +278,27 @@ user_changed (GtkEntry *editable, ESource *source) euri = e_uri_new (uri); g_free (euri->user); + euri->user = NULL; - if (user != NULL) { - euri->user = g_strdup (user); - e_source_set_property (source, "auth", "1"); - } else { - e_source_set_property (source, "auth", NULL); - } - - projection = g_strdup ("/private/full"); + /* two reasons why set readonly to FALSE: + a) the e_source_set_relative_uri does nothing for readonly sources + b) we are going to set default uri, which should be always writeable */ + e_source_set_readonly (source, FALSE); - if (!is_email (user)) { - user = g_strconcat (user, "@gmail.com", NULL); - } + user = gtk_entry_get_text (GTK_ENTRY (editable)); + uri = construct_default_uri (user); + e_source_set_relative_uri (source, uri); + g_free (uri); - e_source_set_relative_uri (source, g_strconcat (CALENDAR_LOCATION, g_strdup(user), g_strdup (projection), NULL)); - e_source_set_property (source, "username", euri->user); + e_source_set_property (source, "username", gtk_entry_get_text (GTK_ENTRY (editable))); e_source_set_property (source, "protocol", "google"); e_source_set_property (source, "auth-domain", "google"); + e_source_set_property (source, "auth", (user && *user) ? "1" : NULL); + e_source_set_property (source, "googlename", NULL); + + /* we changed user, thus reset the chosen calendar combo too, because + other user means other calendars subscribed */ + init_combo_values (GTK_COMBO_BOX (g_object_get_data (G_OBJECT (editable), "CalendarCombo")), _("Default"), NULL); ruri = print_uri_noproto (euri); g_free (ruri); @@ -291,6 +384,229 @@ set_refresh_time (ESource *source, GtkWidget *spin, GtkWidget *option) gtk_spin_button_set_value (GTK_SPIN_BUTTON (spin), time); } +enum { + COL_COLOR = 0, /* GDK_TYPE_COLOR */ + COL_TITLE, /* G_TYPE_STRING */ + COL_URL_PATH, /* G_TYPE_STRING */ + COL_READ_ONLY, /* G_TYPE_BOOLEAN */ + NUM_COLUMNS +}; + +static void +init_combo_values (GtkComboBox *combo, const char *deftitle, const char *defuri) +{ + GtkTreeIter iter; + GtkListStore *store; + + if (!combo) + return; + + store = GTK_LIST_STORE (gtk_combo_box_get_model (combo)); + + gtk_list_store_clear (store); + + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, + COL_COLOR, NULL, + COL_TITLE, deftitle, + COL_URL_PATH, defuri, + COL_READ_ONLY, FALSE, + -1); + + gtk_combo_box_set_active (GTK_COMBO_BOX (combo), 0); +} + +static void +cal_combo_changed (GtkComboBox *combo, ESource *source) +{ + GtkListStore *store; + GtkTreeIter iter; + + g_return_if_fail (combo != NULL); + g_return_if_fail (source != NULL); + + store = GTK_LIST_STORE (gtk_combo_box_get_model (combo)); + + if (gtk_combo_box_get_active_iter (combo, &iter)) { + char *uri = NULL, *title = NULL; + gboolean readonly = FALSE; + + gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, COL_TITLE, &title, COL_URL_PATH, &uri, COL_READ_ONLY, &readonly, -1); + + if (!uri) + uri = construct_default_uri (e_source_get_property (source, "username")); + + if (is_default_uri (uri, e_source_get_property (source, "username"))) { + /* do not store title when we use default uri */ + g_free (title); + title = NULL; + } + + /* first set readonly to FALSE, otherwise if TRUE, then e_source_set_readonly does nothing */ + e_source_set_readonly (source, FALSE); + e_source_set_relative_uri (source, uri); + e_source_set_readonly (source, readonly); + e_source_set_property (source, "googlename", title); + e_source_set_property (source, "protocol", "google"); + e_source_set_property (source, "auth-domain", "google"); + + g_free (title); + g_free (uri); + } +} + +static void +claim_error (GtkWindow *parent, const char *error) +{ + GtkWidget *dialog; + + dialog = gtk_message_dialog_new (parent, + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + error); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); +} + +static void +retrieve_list_clicked (GtkButton *button, GtkComboBox *combo) +{ + ESource *source; + GDataGoogleService *service; + GDataFeed *feed; + char *password, *tmp; + const char *username; + GError *error = NULL; + GtkWindow *parent; + + g_return_if_fail (button != NULL); + g_return_if_fail (combo != NULL); + + parent = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (button))); + + source = g_object_get_data (G_OBJECT (button), "ESource"); + g_return_if_fail (source != NULL); + + username = e_source_get_property (source, "username"); + if (!username || !*username) { + claim_error (parent, _("Please enter user name first.")); + return; + } + + tmp = g_strdup_printf (_("Enter password for user %s to access list of subscribed calendars."), username); + password = e_passwords_ask_password (_("Enter password"), "Calendar", "", tmp, + E_PASSWORDS_REMEMBER_NEVER | E_PASSWORDS_REPROMPT | E_PASSWORDS_SECRET | E_PASSWORDS_DISABLE_REMEMBER, + NULL, parent); + g_free (tmp); + + if (!password) + return; + + service = gdata_google_service_new ("cl", "evolution-client-0.0.1"); + gdata_service_set_credentials (GDATA_SERVICE (service), username, password); + /* privacy... maybe... */ + memset (password, 0, strlen (password)); + g_free (password); + + feed = gdata_service_get_feed (GDATA_SERVICE (service), URL_GET_SUBSCRIBED_CALENDARS, &error); + + if (feed) { + GSList *l; + char *old_selected = NULL; + int idx, active = -1, default_idx = -1; + GtkListStore *store = GTK_LIST_STORE (gtk_combo_box_get_model (combo)); + GtkTreeIter iter; + + if (gtk_combo_box_get_active_iter (combo, &iter)) + gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, COL_URL_PATH, &old_selected, -1); + + gtk_list_store_clear (store); + + for (l = gdata_feed_get_entries (feed), idx = 1; l != NULL; l = l->next) { + const char *uri, *title, *color, *access; + GSList *links; + GDataEntry *entry = (GDataEntry *) l->data; + + if (!entry || !GDATA_IS_ENTRY (entry)) + continue; + + /* skip hidden entries */ + if (gdata_entry_get_custom (entry, "hidden") && g_ascii_strcasecmp (gdata_entry_get_custom (entry, "hidden"), "true") == 0) + continue; + + uri = NULL; + for (links = gdata_entry_get_links (entry); links && !uri; links = links->next) { + GDataEntryLink *link = (GDataEntryLink *)links->data; + + if (!link || !link->href || !link->rel) + continue; + + if (g_ascii_strcasecmp (link->rel, "alternate") == 0) + uri = link->href; + } + + title = gdata_entry_get_title (entry); + color = gdata_entry_get_custom (entry, "color"); + access = gdata_entry_get_custom (entry, "accesslevel"); + + if (uri && title) { + GdkColor gdkcolor; + + if (old_selected && g_str_equal (old_selected, uri)) + active = idx; + + if (color) + gdk_color_parse (color, &gdkcolor); + + if (default_idx == -1 && is_default_uri (uri, username)) { + /* have the default uri always NULL and first in the combo */ + uri = NULL; + gtk_list_store_insert (store, &iter, 0); + default_idx = idx; + } else { + gtk_list_store_append (store, &iter); + } + + gtk_list_store_set (store, &iter, + COL_COLOR, color ? &gdkcolor : NULL, + COL_TITLE, title, + COL_URL_PATH, uri, + COL_READ_ONLY, access && !g_str_equal (access, "owner") && !g_str_equal (access, "contributor"), + -1); + idx++; + } + } + + if (default_idx == -1) { + /* Hey, why we didn't find the default uri? Did something go so wrong or what? */ + gtk_list_store_insert (store, &iter, 0); + gtk_list_store_set (store, &iter, + COL_COLOR, NULL, + COL_TITLE, _("Default"), + COL_URL_PATH, NULL, + COL_READ_ONLY, FALSE, + -1); + } + + gtk_combo_box_set_active (combo, active == -1 ? 0 : active); + + g_free (old_selected); + g_object_unref (feed); + } else { + tmp = g_strdup_printf (_("Cannot read data from Google server.\n%s"), (error && error->message) ? error->message : _("Unknown error.")); + claim_error (parent, tmp); + g_free (tmp); + + if (error) { + g_error_free (error); + error = NULL; + } + } + + g_object_unref (service); +} + GtkWidget * plugin_google (EPlugin *epl, EConfigHookItemFactoryData *data) @@ -305,11 +621,14 @@ plugin_google (EPlugin *epl, GtkWidget *luser; GtkWidget *user; GtkWidget *label; + GtkWidget *combo; char *uri; char *username; const char *ssl_prop; gboolean ssl_enabled; int row; + GtkCellRenderer *renderer; + GtkListStore *store; GtkWidget *option, *spin, *menu, *hbox; GtkWidget *times [4]; @@ -425,7 +744,48 @@ plugin_google (EPlugin *epl, g_free (uri); g_free (username); - return widget; -} + label = gtk_label_new_with_mnemonic (_("Cal_endar:")); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); + gtk_widget_show (label); + gtk_table_attach (GTK_TABLE (parent), label, 0, 1, row + 4, row + 5, GTK_FILL | GTK_EXPAND, 0, 0, 0); + + store = gtk_list_store_new ( + NUM_COLUMNS, + GDK_TYPE_COLOR, /* COL_COLOR */ + G_TYPE_STRING, /* COL_TITLE */ + G_TYPE_STRING, /* COL_URL_PATH */ + G_TYPE_BOOLEAN); /* COL_READ_ONLY */ + + combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL (store)); + + gtk_label_set_mnemonic_widget (GTK_LABEL (label), combo); + + renderer = e_cell_renderer_color_new (); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), renderer, FALSE); + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), renderer, "color", COL_COLOR, NULL); + + renderer = gtk_cell_renderer_text_new (); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), renderer, TRUE); + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), renderer, "text", COL_TITLE, NULL); + + init_combo_values (GTK_COMBO_BOX (combo), + e_source_get_property (source, "googlename") ? e_source_get_property (source, "googlename") : _("Default"), + e_source_get_property (source, "googlename") ? e_source_peek_relative_uri (source) : NULL); + g_signal_connect (combo, "changed", G_CALLBACK (cal_combo_changed), source); + g_object_set_data (G_OBJECT (user), "CalendarCombo", combo); + + hbox = gtk_hbox_new (FALSE, 6); + + gtk_box_pack_start (GTK_BOX (hbox), combo, TRUE, TRUE, 0); + label = gtk_button_new_with_mnemonic (_("Retrieve _list")); + g_signal_connect (label, "clicked", G_CALLBACK (retrieve_list_clicked), combo); + g_object_set_data (G_OBJECT (label), "ESource", source); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + + gtk_widget_show_all (hbox); + gtk_table_attach (GTK_TABLE (parent), hbox, 1, 2, row + 4, row + 5, GTK_FILL | GTK_EXPAND, 0, 0, 0); + + return widget; +} diff --git a/plugins/google-account-setup/org-gnome-evolution-google.eplug.xml b/plugins/google-account-setup/org-gnome-evolution-google.eplug.xml index d6087ad748..93a8aeee2d 100644 --- a/plugins/google-account-setup/org-gnome-evolution-google.eplug.xml +++ b/plugins/google-account-setup/org-gnome-evolution-google.eplug.xml @@ -1,13 +1,19 @@ <?xml version="1.0"?> <e-plugin-list> <e-plugin id="org.gnome.evolution.google" type="shlib" _name="Google sources" - location="@PLUGINDIR@/liborg-gnome-evolution-google@SOEXT@" load-on-startup="false" localedir = "@LOCALEDIR@"> + location="@PLUGINDIR@/liborg-gnome-evolution-google@SOEXT@" load-on-startup="false" localedir = "@LOCALEDIR@" system_plugin="true"> <author name="Ebby Wiselyn" email="ebbywiselyn@gmail.com"/> - <_description>A plugin to setup google calendar.</_description> - + <author name="Joergen Scheibengruber" email="joergen.scheibengruber@googlemail.com"/> + <_description>A plugin to setup google calendar and contacts.</_description> + <hook class="org.gnome.evolution.calendar.config:1.0"> <group target="source" id="org.gnome.evolution.calendar.calendarProperties"> - <item type="item_table" path="00.general/00.source/15.google" factory="plugin_google"/> + <item type="item_table" path="00.general/00.source/15.google" factory="plugin_google"/> + </group> + </hook> + <hook class="org.gnome.evolution.addressbook.config:1.0"> + <group target="source" id="com.novell.evolution.addressbook.config.accountEditor"> + <item type="item" path="00.general/10.display/00.google" factory="plugin_google_contacts"/> </group> </hook> </e-plugin> diff --git a/plugins/groupwise-account-setup/ChangeLog b/plugins/groupwise-account-setup/ChangeLog index 6e158a510c..6c0e4211e6 100644 --- a/plugins/groupwise-account-setup/ChangeLog +++ b/plugins/groupwise-account-setup/ChangeLog @@ -1,3 +1,11 @@ +2008-07-31 Matthew Barnes <mbarnes@redhat.com> + + ** Fixes part of bug #545568 + + * org-gnome-gw-account-setup.eplug.xml: + Add "system_plugin=true" so it's not shown in the Plugin Manager. + This plugin is not designed to be disabled by the user. + 2008-04-30 Chenthill Palanisamy <pchenthill@novell.com> ** Fixes #358644 (bnc) diff --git a/plugins/groupwise-account-setup/org-gnome-gw-account-setup.eplug.xml b/plugins/groupwise-account-setup/org-gnome-gw-account-setup.eplug.xml index dd7886348c..3174226137 100644 --- a/plugins/groupwise-account-setup/org-gnome-gw-account-setup.eplug.xml +++ b/plugins/groupwise-account-setup/org-gnome-gw-account-setup.eplug.xml @@ -1,7 +1,7 @@ <?xml version="1.0"?> <e-plugin-list> <e-plugin id="org.gnome.evolution.plugin.gw-account-setup" type="shlib" _name="Groupwise Account Setup" - location="@PLUGINDIR@/liborg-gnome-gw-account-setup@SOEXT@" load-on-startup="true"> + location="@PLUGINDIR@/liborg-gnome-gw-account-setup@SOEXT@" load-on-startup="true" system_plugin="true"> <author name="Sivaiah Nallagatla" email="snallagatla@novell.com"/> <_description>A plugin to setup groupwise calendar and contacts sources.</_description> diff --git a/plugins/groupwise-features/ChangeLog b/plugins/groupwise-features/ChangeLog index f0587b07df..1b18e0c3f2 100644 --- a/plugins/groupwise-features/ChangeLog +++ b/plugins/groupwise-features/ChangeLog @@ -1,3 +1,18 @@ +2008-08-01 Matthew Barnes <mbarnes@redhat.com> + + ** Fixes bug #544861 + + * org-gnome-proxy-login-errors.xml: + Reword "invalid-user" error message. + +2008-07-28 Milan Crha <mcrha@redhat.com> + + ** Fix for bug #543756 + + * org-gnome-shared-folder.errors.xml: + * process-meeting.c: (process_meeting): + Make question better translatable. + 2008-06-20 Sankar P <psankar@novell.com> * mail-retract.c (retract_mail_settings): diff --git a/plugins/groupwise-features/org-gnome-proxy-login-errors.xml b/plugins/groupwise-features/org-gnome-proxy-login-errors.xml index 2523f51056..36d8527e49 100644 --- a/plugins/groupwise-features/org-gnome-proxy-login-errors.xml +++ b/plugins/groupwise-features/org-gnome-proxy-login-errors.xml @@ -3,7 +3,7 @@ <error id="invalid-user" type="error"> <_primary>Invalid user</_primary> <_secondary> -Proxy login as "{0}" was unsuccessful. Please check Email Id and try again. +Proxy login as "{0}" was unsuccessful. Please check your email address and try again. </_secondary> </error> diff --git a/plugins/groupwise-features/org-gnome-shared-folder.errors.xml b/plugins/groupwise-features/org-gnome-shared-folder.errors.xml index 85ce4ac156..5e7bfb28e5 100644 --- a/plugins/groupwise-features/org-gnome-shared-folder.errors.xml +++ b/plugins/groupwise-features/org-gnome-shared-folder.errors.xml @@ -15,9 +15,17 @@ You cannot share folder with specified user "{0}" </_secondary> </error> -<error id="recurrence" type="question" default="GTK_RESPONSE_YES"> +<error id="recurrence-accept" type="question" default="GTK_RESPONSE_YES"> <_primary>This is a recurring meeting</_primary> -<_secondary>What would you like to {0}?</_secondary> +<_secondary>Would you like to accept it?</_secondary> + <button stock="gtk-cancel" response="GTK_RESPONSE_CANCEL"/> + <button label="This instance" response="GTK_RESPONSE_NO"/> + <button label="All instances" response="GTK_RESPONSE_YES"/> +</error> + +<error id="recurrence-decline" type="question" default="GTK_RESPONSE_YES"> +<_primary>This is a recurring meeting</_primary> +<_secondary>Would you like to decline it?</_secondary> <button stock="gtk-cancel" response="GTK_RESPONSE_CANCEL"/> <button label="This instance" response="GTK_RESPONSE_NO"/> <button label="All instances" response="GTK_RESPONSE_YES"/> diff --git a/plugins/groupwise-features/process-meeting.c b/plugins/groupwise-features/process-meeting.c index 5b8da30b7e..f4606439ce 100644 --- a/plugins/groupwise-features/process-meeting.c +++ b/plugins/groupwise-features/process-meeting.c @@ -221,14 +221,14 @@ process_meeting (ECalendarView *cal_view, icalparameter_partstat status) if (recurring) { gint response; - const char *arg; + const char *msg; if (status == ICAL_PARTSTAT_ACCEPTED || status == ICAL_PARTSTAT_TENTATIVE) - arg = "accept"; + msg = "org.gnome.evolution.mail_shared_folder:recurrence-accept"; else - arg = "decline"; + msg = "org.gnome.evolution.mail_shared_folder:recurrence-decline"; - response = e_error_run (NULL, "org.gnome.evolution.mail_shared_folder:recurrence", arg, NULL); + response = e_error_run (NULL, msg, NULL); if (response == GTK_RESPONSE_YES) { icalproperty *prop; const char *uid = icalcomponent_get_uid (r_data->icalcomp); diff --git a/plugins/hula-account-setup/ChangeLog b/plugins/hula-account-setup/ChangeLog index 5ada817be3..de0ceee8de 100644 --- a/plugins/hula-account-setup/ChangeLog +++ b/plugins/hula-account-setup/ChangeLog @@ -1,3 +1,11 @@ +2008-07-31 Matthew Barnes <mbarnes@redhat.com> + + ** Fixes part of bug #545568 + + * org-gnome-hula-account-setup.eplug.xml: + Add "system_plugin=true" so it's not shown in the Plugin Manager. + This plugin is not designed to be disabled by the user. + 2007-10-26 Kjartan Maraas <kmaraas@gnome.org> * camel-hula-listener.c: (camel_hula_listener_new): diff --git a/plugins/hula-account-setup/org-gnome-evolution-hula-account-setup.eplug.xml b/plugins/hula-account-setup/org-gnome-evolution-hula-account-setup.eplug.xml index 9a0c92e891..24b3467d19 100644 --- a/plugins/hula-account-setup/org-gnome-evolution-hula-account-setup.eplug.xml +++ b/plugins/hula-account-setup/org-gnome-evolution-hula-account-setup.eplug.xml @@ -1,7 +1,7 @@ <?xml version="1.0"?> <e-plugin-list> <e-plugin id="org.gnome.evolution.plugin.hula-account-setup" type="shlib" _name="Hula Account Setup" - location="@PLUGINDIR@/liborg-gnome-evolution-hula-account-setup@SOEXT@" load-on-startup="true"> + location="@PLUGINDIR@/liborg-gnome-evolution-hula-account-setup@SOEXT@" load-on-startup="true" system_plugin="true"> <author name="Harish Krishnaswamy" email="kharish@novell.com"/> <_description>A plugin to setup hula calendar sources.</_description> diff --git a/plugins/itip-formatter/ChangeLog b/plugins/itip-formatter/ChangeLog index fbcd25aa96..6d6d93f1e2 100644 --- a/plugins/itip-formatter/ChangeLog +++ b/plugins/itip-formatter/ChangeLog @@ -1,3 +1,22 @@ +2008-07-28 Milan Crha <mcrha@redhat.com> + + ** Fix for bug #491176 + + * itip-view.c: (itip_view_init): Word-wrap the summary if necessary; + expand also value-labels in the table, thus the text will be aligned + on the left; align action buttons on the left too. + +2008-07-17 Chenthill Palanisamy <pchenthill@novell.com> + + * itip-formatter.c: (view_response_cb): Added some + FIXME's for code cleanup. + +2008-07-16 Sankar P <psankar@novell.com> + + Pushing disk summary changes from the madagascar branch + + * itip-formatter.c (view_response_cb): + 2008-05-29 Milan Crha <mcrha@redhat.com> ** Fix for bug #535459 diff --git a/plugins/itip-formatter/itip-formatter.c b/plugins/itip-formatter/itip-formatter.c index 5447ac3537..d89d74ccf5 100644 --- a/plugins/itip-formatter/itip-formatter.c +++ b/plugins/itip-formatter/itip-formatter.c @@ -1652,6 +1652,7 @@ view_response_cb (GtkWidget *widget, ItipViewResponse response, gpointer data) icalcomponent_add_property (pitip->ical_comp, prop); } + /*FIXME Save schedules is misused here, remove it */ save_schedules = e_cal_get_save_schedules (pitip->current_ecal); switch (response) { @@ -1713,6 +1714,7 @@ view_response_cb (GtkWidget *widget, ItipViewResponse response, gpointer data) break; } + /* FIXME Remove this and handle this at the groupwise mail provider */ if (delete_invitation_from_cache) { CamelFolderChangeInfo *changes = NULL; const char *tag = NULL; diff --git a/plugins/itip-formatter/itip-view.c b/plugins/itip-formatter/itip-view.c index d130d3a6f1..061e1daf6b 100644 --- a/plugins/itip-formatter/itip-view.c +++ b/plugins/itip-formatter/itip-view.c @@ -995,8 +995,10 @@ itip_view_init (ItipView *view) /* Summary */ priv->summary_label = gtk_label_new (NULL); gtk_misc_set_alignment (GTK_MISC (priv->summary_label), 0, 0.5); + gtk_label_set_line_wrap_mode (GTK_LABEL (priv->summary_label), PANGO_WRAP_WORD); + gtk_label_set_line_wrap (GTK_LABEL (priv->summary_label), TRUE); gtk_widget_show (priv->summary_label); - gtk_table_attach (GTK_TABLE (table), priv->summary_label, 0, 2, 0, 1, GTK_FILL, 0, 0, 0); + gtk_table_attach (GTK_TABLE (table), priv->summary_label, 0, 2, 0, 1, GTK_FILL | GTK_EXPAND, 0, 0, 0); /* Location */ priv->location_header = gtk_label_new (_("Location:")); @@ -1004,7 +1006,7 @@ itip_view_init (ItipView *view) gtk_misc_set_alignment (GTK_MISC (priv->location_header), 0, 0.5); gtk_misc_set_alignment (GTK_MISC (priv->location_label), 0, 0.5); gtk_table_attach (GTK_TABLE (table), priv->location_header, 0, 1, 1, 2, GTK_FILL, 0, 0, 0); - gtk_table_attach (GTK_TABLE (table), priv->location_label, 1, 2, 1, 2, GTK_FILL, 0, 0, 0); + gtk_table_attach (GTK_TABLE (table), priv->location_label, 1, 2, 1, 2, GTK_FILL | GTK_EXPAND, 0, 0, 0); /* Start time */ priv->start_header = gtk_label_new (_("Start time:")); @@ -1013,7 +1015,7 @@ itip_view_init (ItipView *view) gtk_misc_set_alignment (GTK_MISC (priv->start_label), 0, 0.5); gtk_widget_show (priv->start_header); gtk_table_attach (GTK_TABLE (table), priv->start_header, 0, 1, 2, 3, GTK_FILL, 0, 0, 0); - gtk_table_attach (GTK_TABLE (table), priv->start_label, 1, 2, 2, 3, GTK_FILL, 0, 0, 0); + gtk_table_attach (GTK_TABLE (table), priv->start_label, 1, 2, 2, 3, GTK_FILL | GTK_EXPAND, 0, 0, 0); /* End time */ priv->end_header = gtk_label_new (_("End time:")); @@ -1021,7 +1023,7 @@ itip_view_init (ItipView *view) gtk_misc_set_alignment (GTK_MISC (priv->end_header), 0, 0.5); gtk_misc_set_alignment (GTK_MISC (priv->end_label), 0, 0.5); gtk_table_attach (GTK_TABLE (table), priv->end_header, 0, 1, 3, 4, GTK_FILL, 0, 0, 0); - gtk_table_attach (GTK_TABLE (table), priv->end_label, 1, 2, 3, 4, GTK_FILL, 0, 0, 0); + gtk_table_attach (GTK_TABLE (table), priv->end_label, 1, 2, 3, 4, GTK_FILL | GTK_EXPAND, 0, 0, 0); /* Status */ priv->status_header = gtk_label_new (_("Status:")); @@ -1029,7 +1031,7 @@ itip_view_init (ItipView *view) gtk_misc_set_alignment (GTK_MISC (priv->status_header), 0, 0.5); gtk_misc_set_alignment (GTK_MISC (priv->status_label), 0, 0.5); gtk_table_attach (GTK_TABLE (table), priv->status_header, 0, 1, 4, 5, GTK_FILL, 0, 0, 0); - gtk_table_attach (GTK_TABLE (table), priv->status_label, 1, 2, 4, 5, GTK_FILL, 0, 0, 0); + gtk_table_attach (GTK_TABLE (table), priv->status_label, 1, 2, 4, 5, GTK_FILL | GTK_EXPAND, 0, 0, 0); /* Comment */ priv->comment_header = gtk_label_new (_("Comment:")); @@ -1037,7 +1039,7 @@ itip_view_init (ItipView *view) gtk_misc_set_alignment (GTK_MISC (priv->comment_header), 0, 0.5); gtk_misc_set_alignment (GTK_MISC (priv->comment_label), 0, 0.5); gtk_table_attach (GTK_TABLE (table), priv->comment_header, 0, 1, 5, 6, GTK_FILL, 0, 0, 0); - gtk_table_attach (GTK_TABLE (table), priv->comment_label, 1, 2, 5, 6, GTK_FILL, 0, 0, 0); + gtk_table_attach (GTK_TABLE (table), priv->comment_label, 1, 2, 5, 6, GTK_FILL | GTK_EXPAND, 0, 0, 0); /* Upper Info items */ priv->upper_info_box = gtk_vbox_new (FALSE, 12); @@ -1119,7 +1121,7 @@ itip_view_init (ItipView *view) /* The buttons for actions */ priv->button_box = gtk_hbutton_box_new (); - gtk_button_box_set_layout (GTK_BUTTON_BOX (priv->button_box), GTK_BUTTONBOX_END); + gtk_button_box_set_layout (GTK_BUTTON_BOX (priv->button_box), GTK_BUTTONBOX_START); gtk_box_set_spacing (GTK_BOX (priv->button_box), 12); gtk_widget_show (priv->button_box); gtk_box_pack_start (GTK_BOX (vbox), priv->button_box, FALSE, FALSE, 0); diff --git a/plugins/mail-notification/ChangeLog b/plugins/mail-notification/ChangeLog index b14337ce9a..278f8bb93a 100644 --- a/plugins/mail-notification/ChangeLog +++ b/plugins/mail-notification/ChangeLog @@ -1,3 +1,34 @@ +2008-08-01 Matthew Barnes <mbarnes@redhat.com> + + ** Fixes part of bug #514006 + + * apps-evolution-mail-notification.schemas.in: + Quote literal values. + +2008-08-01 Milan Crha <mcrha@redhat.com> + + ** Part of fix for bug #353927 + + * mail-notification.c: (icon_activated), (stop_blinking_cb), + (new_notify_status): Do not blink the icon more than 15 seconds. + +2008-07-22 Milan Crha <mcrha@redhat.com> + + ** Part of fix for bug #544022 + + * Makefile.am: + * mail-notification.c: (send_dbus_message): Do not redefine + DBUS_VERSION define, it's supplied by dbus itself. + +2008-07-16 Milan Crha <mcrha@redhat.com> + + ** Fix for bug #543134 + + * mail-notification.c: (get_cfg_widget), (do_properties), + (popup_menu_status), (new_notify_status), + (e_plugin_lib_get_configure_widget): + Show popup menu when right-click over the status icon. + 2008-01-28 Johnny Jacob <jjohnny@novell.com> * mail-notification.c (new_notify_status): Adding proper diff --git a/plugins/mail-notification/Makefile.am b/plugins/mail-notification/Makefile.am index 38b4f051f7..86fc91cc11 100644 --- a/plugins/mail-notification/Makefile.am +++ b/plugins/mail-notification/Makefile.am @@ -5,7 +5,7 @@ INCLUDES = \ if ENABLE_DBUS INCLUDES += -DDBUS_API_SUBJECT_TO_CHANGE=1 \ - -DDBUS_VERSION=$(DBUS_VERSION) \ + -DFOUND_DBUS_VERSION=$(FOUND_DBUS_VERSION) \ $(NMN_CFLAGS) endif diff --git a/plugins/mail-notification/apps-evolution-mail-notification.schemas.in b/plugins/mail-notification/apps-evolution-mail-notification.schemas.in index 4bdafcfec1..c0a038068d 100644 --- a/plugins/mail-notification/apps-evolution-mail-notification.schemas.in +++ b/plugins/mail-notification/apps-evolution-mail-notification.schemas.in @@ -77,7 +77,7 @@ <default>true</default> <locale name="C"> <short>Beep or play sound file.</short> - <long>If true, then beep, otherwise will play sound file when new messages arrive.</long> + <long>If "true", then beep, otherwise will play sound file when new messages arrive.</long> </locale> </schema> <schema> diff --git a/plugins/mail-notification/mail-notification.c b/plugins/mail-notification/mail-notification.c index 8af29023b8..afa633c999 100644 --- a/plugins/mail-notification/mail-notification.c +++ b/plugins/mail-notification/mail-notification.c @@ -55,6 +55,7 @@ #define GCONF_KEY_ENABLED_SOUND GCONF_KEY_ROOT "sound-enabled" static gboolean enabled = FALSE; +static GtkWidget *get_cfg_widget (void); /** * each part should "implement" its own "public" functions: @@ -135,7 +136,7 @@ send_dbus_message (const char *name, const char *data, guint new) /* Appends the data as an argument to the message */ dbus_message_append_args (message, -#if DBUS_VERSION >= 310 +#if FOUND_DBUS_VERSION >= 310 DBUS_TYPE_STRING, &data, #else DBUS_TYPE_STRING, data, @@ -145,7 +146,7 @@ send_dbus_message (const char *name, const char *data, guint new) if (new) { char * display_name = em_utils_folder_name_from_uri (data); dbus_message_append_args (message, -#if DBUS_VERSION >= 310 +#if FOUND_DBUS_VERSION >= 310 DBUS_TYPE_STRING, &display_name, DBUS_TYPE_UINT32, &new, #else DBUS_TYPE_STRING, display_name, DBUS_TYPE_UINT32, new, @@ -270,6 +271,7 @@ get_config_widget_dbus (void) #define GCONF_KEY_STATUS_NOTIFICATION GCONF_KEY_ROOT "status-notification" static GtkStatusIcon *status_icon = NULL; +static guint blink_timeout_id = 0; static unsigned int status_count = 0; #ifdef HAVE_LIBNOTIFY @@ -294,6 +296,11 @@ icon_activated (GtkStatusIcon *icon, gpointer pnotify) gtk_status_icon_set_visible (status_icon, FALSE); g_object_unref (status_icon); + if (blink_timeout_id) { + g_source_remove (blink_timeout_id); + blink_timeout_id = 0; + } + status_icon = NULL; status_count = 0; } @@ -306,6 +313,17 @@ notification_callback (gpointer notify) } #endif +static gboolean +stop_blinking_cb (gpointer data) +{ + blink_timeout_id = 0; + + if (status_icon) + gtk_status_icon_set_blinking (status_icon, FALSE); + + return FALSE; +} + struct _StatusConfigureWidgets { GtkWidget *enable; @@ -348,11 +366,88 @@ toggled_status_cb (GtkWidget *widget, gpointer data) /* ------------------------------------------------------------------- */ static void +do_properties (GtkMenuItem *item, gpointer user_data) +{ + GtkWidget *cfg, *dialog, *vbox, *label, *hbox; + char *text; + + cfg = get_cfg_widget (); + if (!cfg) + return; + + text = g_strconcat ("<span size=\"x-large\">", _("Evolution's Mail Notification"), "</span>", NULL); + + vbox = gtk_vbox_new (FALSE, 10); + label = gtk_label_new (NULL); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); + gtk_label_set_use_markup (GTK_LABEL (label), TRUE); + gtk_label_set_markup (GTK_LABEL (label), text); + g_free (text); + + gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0); + gtk_widget_show (label); + gtk_widget_show (vbox); + + hbox = gtk_hbox_new (FALSE, 10); + label = gtk_label_new (" "); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + gtk_widget_show_all (hbox); + + gtk_box_pack_start (GTK_BOX (hbox), cfg, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0); + + dialog = gtk_dialog_new_with_buttons (_("Mail Notification Properties"), + NULL, + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, + NULL); + + gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE); + gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), vbox); + gtk_container_set_border_width (GTK_CONTAINER (vbox), 10); + gtk_widget_set_size_request (dialog, 400, -1); + g_signal_connect_swapped (dialog, "response", G_CALLBACK (gtk_widget_destroy), dialog); + gtk_widget_show (dialog); +} + +static void +popup_menu_status (GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer user_data) +{ + GtkMenu *menu; + GtkWidget *item; + + menu = GTK_MENU (gtk_menu_new ()); + + item = gtk_image_menu_item_new_from_stock (GTK_STOCK_CLOSE, NULL); + #ifdef HAVE_LIBNOTIFY + g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (icon_activated), notify); + #else + g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (icon_activated), NULL); + #endif + gtk_widget_show (item); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + + item = gtk_separator_menu_item_new (); + gtk_widget_show (item); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + + item = gtk_image_menu_item_new_from_stock (GTK_STOCK_PROPERTIES, NULL); + g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (do_properties), NULL); + gtk_widget_show (item); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + + g_object_ref_sink (menu); + gtk_menu_popup (menu, NULL, NULL, NULL, NULL, button, activate_time); + g_object_unref (menu); +} + +static void new_notify_status (EMEventTargetFolder *t) { char *msg; + gboolean new_icon = !status_icon; - if (!status_icon) { + if (new_icon) { status_icon = gtk_status_icon_new (); gtk_status_icon_set_from_pixbuf (status_icon, e_icon_factory_get_icon ("mail-unread", E_ICON_SIZE_LARGE_TOOLBAR)); } @@ -370,8 +465,13 @@ new_notify_status (EMEventTargetFolder *t) } gtk_status_icon_set_tooltip (status_icon, msg); + + if (new_icon && is_part_enabled (GCONF_KEY_STATUS_BLINK)) { + gtk_status_icon_set_blinking (status_icon, TRUE); + blink_timeout_id = g_timeout_add_seconds (15, stop_blinking_cb, NULL); + } + gtk_status_icon_set_visible (status_icon, TRUE); - gtk_status_icon_set_blinking (status_icon, is_part_enabled (GCONF_KEY_STATUS_BLINK)); #ifdef HAVE_LIBNOTIFY /* Now check whether we're supposed to send notifications */ @@ -399,6 +499,8 @@ new_notify_status (EMEventTargetFolder *t) #else g_signal_connect (G_OBJECT (status_icon), "activate", G_CALLBACK (icon_activated), NULL); #endif + + g_signal_connect (G_OBJECT (status_icon), "popup-menu", G_CALLBACK (popup_menu_status), NULL); } static void @@ -694,6 +796,37 @@ toggled_only_inbox_cb (GtkWidget *widget, gpointer data) set_part_enabled (GCONF_KEY_NOTIFY_ONLY_INBOX, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget))); } +static GtkWidget * +get_cfg_widget (void) +{ + GtkWidget *cfg, *vbox, *check; + + vbox = gtk_vbox_new (FALSE, 6); + check = gtk_check_button_new_with_mnemonic (_("Notify new messages for _Inbox only")); + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), is_part_enabled (GCONF_KEY_NOTIFY_ONLY_INBOX)); + g_signal_connect (G_OBJECT (check), "toggled", G_CALLBACK (toggled_only_inbox_cb), NULL); + gtk_widget_show (check); + gtk_box_pack_start (GTK_BOX (vbox), check, FALSE, FALSE, 0); + +#ifdef HAVE_DBUS + cfg = get_config_widget_dbus (); + if (cfg) + gtk_box_pack_start (GTK_BOX (vbox), cfg, FALSE, FALSE, 0); +#endif + cfg = get_config_widget_status (); + if (cfg) + gtk_box_pack_start (GTK_BOX (vbox), cfg, FALSE, FALSE, 0); + + cfg = get_config_widget_sound (); + if (cfg) + gtk_box_pack_start (GTK_BOX (vbox), cfg, FALSE, FALSE, 0); + + gtk_widget_show (vbox); + + return vbox; +} + void org_gnome_mail_new_notify (EPlugin *ep, EMEventTargetFolder *t); void org_gnome_mail_read_notify (EPlugin *ep, EMEventTargetMessage *t); @@ -779,30 +912,5 @@ e_plugin_lib_enable (EPluginLib *ep, int enable) GtkWidget * e_plugin_lib_get_configure_widget (EPlugin *epl) { - GtkWidget *cfg, *vbox, *check; - - vbox = gtk_vbox_new (FALSE, 6); - check = gtk_check_button_new_with_mnemonic (_("Notify new messages for _Inbox only")); - - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), is_part_enabled (GCONF_KEY_NOTIFY_ONLY_INBOX)); - g_signal_connect (G_OBJECT (check), "toggled", G_CALLBACK (toggled_only_inbox_cb), NULL); - gtk_widget_show (check); - gtk_box_pack_start (GTK_BOX (vbox), check, FALSE, FALSE, 0); - -#ifdef HAVE_DBUS - cfg = get_config_widget_dbus (); - if (cfg) - gtk_box_pack_start (GTK_BOX (vbox), cfg, FALSE, FALSE, 0); -#endif - cfg = get_config_widget_status (); - if (cfg) - gtk_box_pack_start (GTK_BOX (vbox), cfg, FALSE, FALSE, 0); - - cfg = get_config_widget_sound (); - if (cfg) - gtk_box_pack_start (GTK_BOX (vbox), cfg, FALSE, FALSE, 0); - - gtk_widget_show (vbox); - - return vbox; + return get_cfg_widget (); } diff --git a/plugins/mark-all-read/ChangeLog b/plugins/mark-all-read/ChangeLog index bfd604cfbe..caf1e71e70 100644 --- a/plugins/mark-all-read/ChangeLog +++ b/plugins/mark-all-read/ChangeLog @@ -1,3 +1,10 @@ +2008-08-07 Matthew Barnes <mbarnes@redhat.com> + + ** Fixes bug #530402 + + * mark-all-read.c (prompt_user): + Improve dialog wording, and don't use a window title (HIG). + 2008-05-16 Matthew Barnes <mbarnes@redhat.com> ** Fixes bug #514383 diff --git a/plugins/mark-all-read/mark-all-read.c b/plugins/mark-all-read/mark-all-read.c index 8940551e47..af6c482288 100644 --- a/plugins/mark-all-read/mark-all-read.c +++ b/plugins/mark-all-read/mark-all-read.c @@ -34,9 +34,10 @@ #include "e-util/e-error.h" #define PRIMARY_TEXT \ - N_("Mark all messages in this folder and subfolders as read?") + N_("Also mark messages in subfolders?") #define SECONDARY_TEXT \ - N_("Do you want the operation to be performed also in the subfolders?") + N_("Do you want to mark messages as read in the current folder " \ + "only, or in the current folder as well as all subfolders?") void org_gnome_mark_all_read (EPlugin *ep, EMPopupTargetFolder *target); static void mar_got_folder (char *uri, CamelFolder *folder, void *data); @@ -81,8 +82,7 @@ prompt_user (void) dialog = gtk_dialog_new (); gtk_widget_hide (GTK_DIALOG (dialog)->action_area); gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE); - gtk_window_set_title ( - GTK_WINDOW (dialog), _("Mark All Messages as Read")); + gtk_window_set_title (GTK_WINDOW (dialog), ""); g_signal_connect ( dialog, "map", G_CALLBACK (gtk_widget_queue_resize), NULL); diff --git a/plugins/python/ChangeLog b/plugins/python/ChangeLog index 43a0bd3482..063418150e 100644 --- a/plugins/python/ChangeLog +++ b/plugins/python/ChangeLog @@ -1,3 +1,12 @@ +2008-07-21 Johnny Jacob <jjohnny@novell.com> + + * Makefile.am (example_sources): More typo fixes. :( + +2008-07-21 Johnny Jacob <jjohnny@novell.com> + + * Makefile.am (example_SOURCES): Add example sources + EXTRA_DIST. + 2008-06-12 Johnny Jacob <jjohnny@novell.com> * example/org-gnome-hello-python-ui.xml: Added. diff --git a/plugins/python/Makefile.am b/plugins/python/Makefile.am index 854c7915f6..7a65c4586a 100644 --- a/plugins/python/Makefile.am +++ b/plugins/python/Makefile.am @@ -15,7 +15,14 @@ liborg_gnome_evolution_python_la_LIBADD = \ $(PY_LIBS) \ $(E_UTIL_LIBS) -EXTRA_DIST = org-gnome-evolution-python.eplug.xml +example_sources = \ + example/hello_python.py \ + example/org-gnome-hello-python-ui.xml \ + example/org-gnome-hello-python.eplug.xml \ + example/Makefile.am + +EXTRA_DIST = org-gnome-evolution-python.eplug.xml \ + $(example_sources) BUILT_SOURCES = $(plugin_DATA) CLEANFILES = $(BUILT_SOURCES) diff --git a/plugins/startup-wizard/ChangeLog b/plugins/startup-wizard/ChangeLog index 2aad1c410e..faeebb70f1 100644 --- a/plugins/startup-wizard/ChangeLog +++ b/plugins/startup-wizard/ChangeLog @@ -1,3 +1,11 @@ +2008-08-01 Matthew Barnes <mbarnes@redhat.com> + + ** Fixes bug #529460 + + * org-gnome-evolution-startup-wizard.eplug.xml: + Change "Startup Wizard" to "Setup Assistant". Also make the + plugin description less lame. + 2008-06-02 Jacob Brown <jeblinux@gmail.com> ** Fix for bug #529464 diff --git a/plugins/startup-wizard/org-gnome-evolution-startup-wizard.eplug.xml b/plugins/startup-wizard/org-gnome-evolution-startup-wizard.eplug.xml index 0f57b6db89..1197633a23 100644 --- a/plugins/startup-wizard/org-gnome-evolution-startup-wizard.eplug.xml +++ b/plugins/startup-wizard/org-gnome-evolution-startup-wizard.eplug.xml @@ -4,9 +4,9 @@ type="shlib" id="org.gnome.evolution.plugin.startup-wizard" location="@PLUGINDIR@/liborg-gnome-evolution-startup-wizard@SOEXT@" - _name="Startup wizard"> + _name="Setup Assistant"> <author name="JP Rosevear" email="jpr@novell.com"/> - <_description>A plugin that manages the Startup wizard.</_description> + <_description>Guides you through your initial account setup.</_description> <hook class="org.gnome.evolution.shell.events:1.0"> <event id="upgrade.done" diff --git a/plugins/templates/ChangeLog b/plugins/templates/ChangeLog new file mode 100644 index 0000000000..c559689e43 --- /dev/null +++ b/plugins/templates/ChangeLog @@ -0,0 +1,19 @@ +2008-07-21 Johnny Jacob <jjohnny@novell.com> + + * templates.c: Remove unused header files. + + * Makefile.am (INCLUDES): Allow includes from top_builddir. + +2008-07-18 Bharath Acharya <abharath@novell.com> + + ** Fixes Bug #200147 + + Basic functionality implemented by Diego Escalante Urrelo + <diegoe@gnome.org> Everyone owes him a big mug of Beer for that. + + ** Added Templates plugin + * Makefile.am: + * apps-evolution-template-placeholders.schemas.in: + * org-gnome-templates.eplug.xml: + * templates.c: + * templates.glade: diff --git a/plugins/templates/Makefile.am b/plugins/templates/Makefile.am new file mode 100644 index 0000000000..f43dce0992 --- /dev/null +++ b/plugins/templates/Makefile.am @@ -0,0 +1,41 @@ +INCLUDES = \ + -I$(top_srcdir) \ + -I$(top_builddir) \ + -I$(top_builddir)/composer \ + $(EVOLUTION_MAIL_CFLAGS) \ + -DEVOLUTION_GLADEDIR=\""$(gladedir)"\" \ + -DEVOLUTION_PLUGINDIR="\"$(plugindir)\"" + +@EVO_PLUGIN_RULE@ + +plugin_DATA = \ + org-gnome-templates.eplug \ + templates.glade + +plugin_LTLIBRARIES = liborg-gnome-templates.la + +liborg_gnome_templates_la_SOURCES = templates.c +liborg_gnome_templates_la_LDFLAGS = -module -avoid-version + +schemadir = $(GCONF_SCHEMA_FILE_DIR) +schema_in_files = apps-evolution-template-placeholders.schemas.in +schema_DATA = $(schema_in_files:.schemas.in=.schemas) + +@INTLTOOL_SCHEMAS_RULE@ + +install-data-local: + if test -z "$(DESTDIR)" ; then \ + for p in $(schema_DATA) ; do \ + GCONF_CONFIG_SOURCE=$(GCONF_SCHEMA_CONFIG_SOURCE) $(GCONFTOOL) --makefile-install-rule $$p; \ + done \ + fi + +EXTRA_DIST = org-gnome-templates.eplug.xml \ + $(schema_in_files) \ + templates.glade + +BUILT_SOURCES = org-gnome-templates.eplug + +CLEANFILES = $(BUILT_SOURCES) + +DISTCLEANFILES = $(schema_DATA) diff --git a/plugins/templates/apps-evolution-template-placeholders.schemas.in b/plugins/templates/apps-evolution-template-placeholders.schemas.in new file mode 100644 index 0000000000..c6a1f4d07d --- /dev/null +++ b/plugins/templates/apps-evolution-template-placeholders.schemas.in @@ -0,0 +1,26 @@ +<gconfschemafile> + <schemalist> + <schema> + <key>/schemas/apps/evolution/mail/template_placeholders</key> + <applyto>/apps/evolution/mail/template_placeholders</applyto> + <owner>evolution-mail</owner> + <type>list</type> + <list_type>string</list_type> + + <!-- The following are the keyword/value pairs used by the plugin to + substitute the messages stored under the Templates folder. The list can + have any number of such pairs.--> + + <default>[myphone=012345,myplace=Abcd,myname=Alice]</default> + <locale name="C"> + <short>List of keyword/value pairs for the Templates plugin to + substitute in a message body.</short> + <long> + List of keyword/value pairs for the Templates plugin to + substitute in a message body. + </long> + </locale> + </schema> + </schemalist> +</gconfschemafile> + diff --git a/plugins/templates/org-gnome-templates.eplug.xml b/plugins/templates/org-gnome-templates.eplug.xml new file mode 100644 index 0000000000..02b14148ad --- /dev/null +++ b/plugins/templates/org-gnome-templates.eplug.xml @@ -0,0 +1,36 @@ +<?xml version="1.0"?> +<e-plugin-list> + <e-plugin + type="shlib" + id="org.gnome.evolution.plugin.templates" + location="@PLUGINDIR@/liborg-gnome-templates@SOEXT@" + _name="Templates"> + <_description>Drafts based template plugin</_description> + <author name="Bharath Acharya" email="abharath@novell.com"/> + <author name="Diego Escalante Urrelo" email="diegoe@gnome.org"/> + + <!-- hook into the mail popup menu --> + <hook class="org.gnome.evolution.mail.popup:1.0"> + <menu + id="org.gnome.evolution.mail.folderview.popup" + target="select" + factory="org_gnome_templates_popup"> + </menu> + </hook> + + <hook class="org.gnome.evolution.ui:1.0"> + <ui-manager id="org.gnome.evolution.composer"> + <menubar name='main-menu'> + <placeholder name='pre-edit-menu'> + <menu action='file-menu'> + <placeholder name="template-holder"> + <menuitem action="Template"/> + </placeholder> + </menu> + </placeholder> + </menubar> + </ui-manager> + </hook> + </e-plugin> +</e-plugin-list> + diff --git a/plugins/templates/templates.c b/plugins/templates/templates.c new file mode 100644 index 0000000000..c72ae942a9 --- /dev/null +++ b/plugins/templates/templates.c @@ -0,0 +1,763 @@ +/* + * templates.c + * This file is part of Draft Templates plugin for Evolution + * + * Authors: + * Diego Escalante Urrelo <diegoe@gnome.org> + * Bharath Acharya <abharath@novell.com> + * Copyright (C) 2008 - Diego Escalante Urrelo + * Bharath Acharya + * + * Draft Templates is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Draft Templates is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Draft Templates; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#include <gtk/gtk.h> +#include <glib.h> +#include <glib/gi18n.h> +#include <string.h> + +#include <gconf/gconf-client.h> + +#include <e-util/e-config.h> +#include <camel/camel-url.h> +#include <camel/camel-folder.h> +#include <camel/camel-mime-message.h> +#include <camel/camel-multipart.h> +#include <camel/camel-stream-mem.h> +#include <camel/camel-store.h> + +#include <mail/em-composer-utils.h> +#include <mail/em-popup.h> +#include <mail/mail-session.h> +#include <mail/mail-ops.h> +#include <e-util/e-error.h> +#include <e-util/e-plugin.h> +#include <glade/glade.h> + +#include <composer/e-msg-composer.h> + +#define GCONF_KEY_TEMPLATE_PLACEHOLDERS "/apps/evolution/mail/template_placeholders" + +typedef struct { + GladeXML *xml; + GConfClient *gconf; + GtkWidget *treeview; + GtkWidget *clue_add; + GtkWidget *clue_edit; + GtkWidget *clue_remove; + GtkListStore *store; +} UIData; + +enum { + CLUE_KEYWORD_COLUMN, + CLUE_VALUE_COLUMN, + CLUE_N_COLUMNS, +}; + +typedef struct { + CamelMimeMessage *msg; + EMPopupTargetSelect *t; +} UserData; + +static char* get_content (CamelMimeMessage *message); + +static void reply_with_template (EPopup *ep, EPopupItem *item, void *data); + +static void popup_free (EPopup *ep, GSList *l, void *data); + +static GSList *fill_submenu (CamelStore *store, + CamelFolderInfo *info, + GSList *list, + EMPopupTargetSelect *t); + +static GSList *append_to_menu (CamelFolder *folder, + GPtrArray *uids, + GSList *list, + EMPopupTargetSelect *t); + +void org_gnome_templates_popup (EPlugin *ep, EMPopupTargetSelect *t); + +GtkWidget *e_plugin_lib_get_configure_widget (EPlugin *epl); + +gboolean e_plugin_ui_init (GtkUIManager *manager, EMsgComposer *composer); + + +/* Thanks to attachment reminder plugin for this*/ +static void commit_changes (UIData *ui); + +static void key_cell_edited_callback (GtkCellRendererText *cell, gchar *path_string, + gchar *new_text,UIData *ui); + +static void value_cell_edited_callback (GtkCellRendererText *cell, gchar *path_string, + gchar *new_text,UIData *ui); + +static gboolean clue_foreach_check_isempty (GtkTreeModel *model, GtkTreePath + *path, GtkTreeIter *iter, UIData *ui); + +static void +selection_changed (GtkTreeSelection *selection, UIData *ui) +{ + GtkTreeModel *model; + GtkTreeIter iter; + + if (gtk_tree_selection_get_selected (selection, &model, &iter)) { + gtk_widget_set_sensitive (ui->clue_edit, TRUE); + gtk_widget_set_sensitive (ui->clue_remove, TRUE); + } else { + gtk_widget_set_sensitive (ui->clue_edit, FALSE); + gtk_widget_set_sensitive (ui->clue_remove, FALSE); + } +} + +static void +destroy_ui_data (gpointer data) +{ + UIData *ui = (UIData *) data; + + if (!ui) + return; + + g_object_unref (ui->xml); + g_object_unref (ui->gconf); + g_free (ui); +} + +static void +commit_changes (UIData *ui) +{ + GtkTreeModel *model = NULL; + GSList *clue_list = NULL; + GtkTreeIter iter; + gboolean valid; + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (ui->treeview)); + valid = gtk_tree_model_get_iter_first (model, &iter); + + while (valid) { + char *keyword, *value; + char *key; + + gtk_tree_model_get (model, &iter, CLUE_KEYWORD_COLUMN, &keyword, -1); + gtk_tree_model_get (model, &iter, CLUE_VALUE_COLUMN, &value, -1); + + /* Check if the keyword and value are not empty */ + if ((keyword) && (value) && (g_utf8_strlen(g_strstrip(keyword), -1) > 0) + && (g_utf8_strlen(g_strstrip(value), -1) > 0)) { + key = g_strdup_printf("%s=%s", keyword, value); + clue_list = g_slist_append (clue_list, key); + } + valid = gtk_tree_model_iter_next (model, &iter); + } + + gconf_client_set_list (ui->gconf, GCONF_KEY_TEMPLATE_PLACEHOLDERS, GCONF_VALUE_STRING, clue_list, NULL); + + g_slist_foreach (clue_list, (GFunc) g_free, NULL); + g_slist_free (clue_list); +} + +static void +clue_check_isempty (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, UIData *ui) +{ + GtkTreeSelection *selection; + char *keyword = NULL; + gboolean valid; + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (ui->treeview)); + /* move to the previous node */ + valid = gtk_tree_path_prev (path); + + gtk_tree_model_get (model, iter, CLUE_KEYWORD_COLUMN, &keyword, -1); + if ((keyword) && !(g_utf8_strlen (g_strstrip (keyword), -1) > 0)) + gtk_list_store_remove (ui->store, iter); + + /* Check if we have a valid row to select. If not, then select + * the previous row */ + if (gtk_list_store_iter_is_valid (GTK_LIST_STORE (model), iter)) { + gtk_tree_selection_select_iter (selection, iter); + } else { + if (path && valid) { + gtk_tree_model_get_iter (model, iter, path); + gtk_tree_selection_select_iter (selection, iter); + } + } + + gtk_widget_grab_focus (ui->treeview); + g_free (keyword); +} + +static gboolean +clue_foreach_check_isempty (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, UIData *ui) +{ + gboolean valid; + + valid = gtk_tree_model_get_iter_first (model, iter); + while (valid && gtk_list_store_iter_is_valid (ui->store, iter)) { + char *keyword = NULL; + gtk_tree_model_get (model, iter, CLUE_KEYWORD_COLUMN, &keyword, -1); + /* Check if the keyword is not empty and then emit the row-changed + signal (if we delete the row, then the iter gets corrupted) */ + if ((keyword) && !(g_utf8_strlen (g_strstrip (keyword), -1) > 0)) + gtk_tree_model_row_changed (model, path, iter); + + g_free (keyword); + valid = gtk_tree_model_iter_next (model, iter); + } + + return FALSE; +} + +static void +key_cell_edited_callback (GtkCellRendererText *cell, + gchar *path_string, + gchar *new_text, + UIData *ui) +{ + GtkTreeModel *model; + GtkTreeIter iter; + char *value; + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (ui->treeview)); + + gtk_tree_model_get_iter_from_string (model, &iter, path_string); + + gtk_tree_model_get (model, &iter, CLUE_VALUE_COLUMN, &value, -1); + gtk_list_store_set (GTK_LIST_STORE (model), &iter, + CLUE_KEYWORD_COLUMN, new_text, CLUE_VALUE_COLUMN, value, -1); + + commit_changes (ui); +} + +static void +value_cell_edited_callback (GtkCellRendererText *cell, + gchar *path_string, + gchar *new_text, + UIData *ui) +{ + GtkTreeModel *model; + GtkTreeIter iter; + char *keyword; + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (ui->treeview)); + + gtk_tree_model_get_iter_from_string (model, &iter, path_string); + + gtk_tree_model_get (model, &iter, CLUE_KEYWORD_COLUMN, &keyword, -1); + + gtk_list_store_set (GTK_LIST_STORE (model), &iter, + CLUE_KEYWORD_COLUMN, keyword, CLUE_VALUE_COLUMN, new_text, -1); + + commit_changes (ui); +} + +static void +clue_add_clicked (GtkButton *button, UIData *ui) +{ + GtkTreeModel *model; + GtkTreeIter iter; + gchar *new_clue = NULL; + GtkTreeViewColumn *focus_col; + GtkTreePath *path; + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (ui->treeview)); + gtk_tree_model_foreach (model, (GtkTreeModelForeachFunc) clue_foreach_check_isempty, ui); + + /* Disconnect from signal so that we can create an empty row */ + g_signal_handlers_disconnect_matched(G_OBJECT(model), G_SIGNAL_MATCH_FUNC, 0, 0, NULL, clue_check_isempty, ui); + + /* TODO : Trim and check for blank strings */ + new_clue = g_strdup (""); + gtk_list_store_append (GTK_LIST_STORE (model), &iter); + gtk_list_store_set (GTK_LIST_STORE (model), &iter, + CLUE_KEYWORD_COLUMN, new_clue, CLUE_VALUE_COLUMN, new_clue, -1); + + focus_col = gtk_tree_view_get_column (GTK_TREE_VIEW (ui->treeview), CLUE_KEYWORD_COLUMN); + path = gtk_tree_model_get_path (model, &iter); + + if (path) { + gtk_tree_view_set_cursor (GTK_TREE_VIEW (ui->treeview), path, focus_col, TRUE); + gtk_tree_view_row_activated(GTK_TREE_VIEW(ui->treeview), path, focus_col); + gtk_tree_path_free (path); + } + + /* We have done our job, connect back to the signal */ + g_signal_connect(G_OBJECT(model), "row-changed", G_CALLBACK(clue_check_isempty), ui); +} + +static void +clue_remove_clicked (GtkButton *button, UIData *ui) +{ + GtkTreeSelection *selection; + GtkTreeModel *model; + GtkTreeIter iter; + GtkTreePath *path; + gboolean valid; + gint len; + + valid = FALSE; + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (ui->treeview)); + if (!gtk_tree_selection_get_selected (selection, &model, &iter)) + return; + + /* Get the path and move to the previous node :) */ + path = gtk_tree_model_get_path (model, &iter); + if (path) + valid = gtk_tree_path_prev(path); + + gtk_list_store_remove (GTK_LIST_STORE (model), &iter); + + len = gtk_tree_model_iter_n_children (model, NULL); + if (len > 0) { + if (gtk_list_store_iter_is_valid (GTK_LIST_STORE(model), &iter)) { + gtk_tree_selection_select_iter (selection, &iter); + } else { + if (path && valid) { + gtk_tree_model_get_iter(model, &iter, path); + gtk_tree_selection_select_iter (selection, &iter); + } + } + } else { + gtk_widget_set_sensitive (ui->clue_edit, FALSE); + gtk_widget_set_sensitive (ui->clue_remove, FALSE); + } + + gtk_widget_grab_focus(ui->treeview); + gtk_tree_path_free (path); + + commit_changes (ui); +} + +static void +clue_edit_clicked (GtkButton *button, UIData *ui) +{ + GtkTreeSelection *selection; + GtkTreeModel *model; + GtkTreePath *path; + GtkTreeIter iter; + GtkTreeViewColumn *focus_col; + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (ui->treeview)); + if (!gtk_tree_selection_get_selected (selection, &model, &iter)) + return; + + focus_col = gtk_tree_view_get_column (GTK_TREE_VIEW (ui->treeview), CLUE_KEYWORD_COLUMN); + path = gtk_tree_model_get_path (model, &iter); + + if (path) { + gtk_tree_view_set_cursor (GTK_TREE_VIEW (ui->treeview), path, focus_col, TRUE); + gtk_tree_path_free (path); + } +} + +GtkWidget * +e_plugin_lib_get_configure_widget (EPlugin *epl) +{ + GtkCellRenderer *renderer_key, *renderer_value; + GtkTreeSelection *selection; + GtkTreeIter iter; + GConfClient *gconf = gconf_client_get_default(); + GtkWidget *hbox; + GSList *clue_list = NULL, *list; + GtkTreeModel *model; + + UIData *ui = g_new0 (UIData, 1); + + char *gladefile; + + gladefile = g_build_filename (EVOLUTION_PLUGINDIR, + "templates.glade", + NULL); + ui->xml = glade_xml_new (gladefile, "templates_configuration_box", NULL); + g_free (gladefile); + + ui->gconf = gconf_client_get_default (); + + ui->treeview = glade_xml_get_widget (ui->xml, "clue_treeview"); + + ui->store = gtk_list_store_new (CLUE_N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING); + + gtk_tree_view_set_model (GTK_TREE_VIEW (ui->treeview), GTK_TREE_MODEL (ui->store)); + + renderer_key = gtk_cell_renderer_text_new (); + gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (ui->treeview), -1, _("Keywords"), + renderer_key, "text", CLUE_KEYWORD_COLUMN, NULL); + g_object_set (G_OBJECT (renderer_key), "editable", TRUE, NULL); + g_signal_connect(renderer_key, "edited", (GCallback) key_cell_edited_callback, ui); + + renderer_value = gtk_cell_renderer_text_new (); + gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (ui->treeview), -1, _("Values"), + renderer_value, "text", CLUE_VALUE_COLUMN, NULL); + g_object_set (G_OBJECT (renderer_value), "editable", TRUE, NULL); + g_signal_connect(renderer_value, "edited", (GCallback) value_cell_edited_callback, ui); + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (ui->treeview)); + gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE); + g_signal_connect (G_OBJECT (selection), "changed", G_CALLBACK (selection_changed), ui); + gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (ui->treeview), TRUE); + + ui->clue_add = glade_xml_get_widget (ui->xml, "clue_add"); + g_signal_connect (G_OBJECT (ui->clue_add), "clicked", G_CALLBACK (clue_add_clicked), ui); + + ui->clue_remove = glade_xml_get_widget (ui->xml, "clue_remove"); + g_signal_connect (G_OBJECT (ui->clue_remove), "clicked", G_CALLBACK (clue_remove_clicked), ui); + gtk_widget_set_sensitive (ui->clue_remove, FALSE); + + ui->clue_edit = glade_xml_get_widget (ui->xml, "clue_edit"); + g_signal_connect (G_OBJECT (ui->clue_edit), "clicked", G_CALLBACK (clue_edit_clicked), ui); + gtk_widget_set_sensitive (ui->clue_edit, FALSE); + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (ui->treeview)); + g_signal_connect(G_OBJECT(model), "row-changed", G_CALLBACK(clue_check_isempty), ui); + + /* Populate tree view with values from gconf */ + clue_list = gconf_client_get_list ( gconf, GCONF_KEY_TEMPLATE_PLACEHOLDERS, GCONF_VALUE_STRING, NULL ); + + for (list = clue_list; list; list = g_slist_next (list)) { + char **temp = g_strsplit (list->data, "=", 2); + gtk_list_store_append (ui->store, &iter); + gtk_list_store_set (ui->store, &iter, CLUE_KEYWORD_COLUMN, temp[0], CLUE_VALUE_COLUMN, temp[1], -1); + g_strfreev(temp); + } + + if (clue_list) { + g_slist_foreach (clue_list, (GFunc) g_free, NULL); + g_slist_free (clue_list); + } + + /* Add the list here */ + + hbox = gtk_vbox_new (FALSE, 0); + + gtk_box_pack_start (GTK_BOX (hbox), glade_xml_get_widget (ui->xml, "templates_configuration_box"), TRUE, TRUE, 0); + + /* to let free data properly on destroy of configuration widget */ + g_object_set_data_full (G_OBJECT (hbox), "myui-data", ui, destroy_ui_data); + + return hbox; +} + +/* borrowed from plugins/mail-to-task/ */ +static char * +get_content (CamelMimeMessage *message) +{ + CamelDataWrapper *content; + CamelStream *mem; + CamelContentType *type; + CamelMimePart *mime_part = CAMEL_MIME_PART (message); + char *str, *convert_str = NULL; + gsize bytes_read, bytes_written; + gint count = 2; + + content = camel_medium_get_content_object ((CamelMedium *) message); + if (!content) + return NULL; + + /* Get non-multipart content from multipart message. */ + while (CAMEL_IS_MULTIPART (content) && count > 0) { + mime_part = camel_multipart_get_part (CAMEL_MULTIPART (content), 0); + content = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part)); + count--; + } + + if (!mime_part) + return NULL; + + type = camel_mime_part_get_content_type (mime_part); + if (!camel_content_type_is (type, "text", "plain")) + return NULL; + + mem = camel_stream_mem_new (); + camel_data_wrapper_decode_to_stream (content, mem); + + str = g_strndup ((const gchar*)((CamelStreamMem *) mem)->buffer->data, ((CamelStreamMem *) mem)->buffer->len); + camel_object_unref (mem); + + /* convert to UTF-8 string */ + if (str && content->mime_type->params && content->mime_type->params->value) { + convert_str = g_convert (str, strlen (str), + "UTF-8", content->mime_type->params->value, + &bytes_read, &bytes_written, NULL); + } + + if (convert_str) { + g_free (str); + return convert_str; + } + else + return str; + +} + +static void +reply_with_template (EPopup *ep, EPopupItem *item, void *data) +{ + CamelMimeMessage *new, *template, *reply_to; + CamelStore *store; + CamelFolder *templates_folder; + struct _camel_header_raw *header; + UserData *userdata = item->user_data; + char *cont, *basedir, *url; + + /* We get the templates folder and all the uids of the messages in there */ + basedir = g_build_filename (g_get_home_dir (), ".evolution", "mail", "local", NULL); + url = g_strdup_printf ("mbox://%s", basedir); + g_free (basedir); + + store = (CamelStore *) camel_session_get_service (session, url, CAMEL_PROVIDER_STORE, NULL); + g_free (url); + + templates_folder = camel_store_get_folder (store, _("Templates"), CAMEL_STORE_FOLDER_CREATE, NULL); + + /* Get from the currently selected folder, the currently selected message */ + reply_to = camel_folder_get_message (userdata->t->folder, + g_ptr_array_index (userdata->t->uids, 0), + NULL); + + /* The message we'll be using has been stored when building the menu */ + template = userdata->msg; + + /* The new message we are creating */ + new = camel_mime_message_new(); + + /* Add the headers from the message we are replying to, so CC and that + * stuff is preserved. + */ + header = ((CamelMimePart *)reply_to)->headers; + while (header) { + if (g_ascii_strncasecmp (header->name, "content-", 8) != 0) { + camel_medium_add_header((CamelMedium *) new, + header->name, + header->value); + } + header = header->next; + } + + camel_mime_part_set_encoding((CamelMimePart *) new, CAMEL_TRANSFER_ENCODING_8BIT); + + /* Get the template content. */ + cont = get_content (template); + + /* Set the To: field to the same To: field of the message we are replying to. */ + camel_mime_message_set_recipients (new, CAMEL_RECIPIENT_TYPE_TO, + camel_mime_message_get_from (reply_to)); + + + /* Copy the CC and BCC from the template.*/ + camel_mime_message_set_recipients (new, CAMEL_RECIPIENT_TYPE_CC, + camel_mime_message_get_recipients (template, CAMEL_RECIPIENT_TYPE_CC)); + + camel_mime_message_set_recipients (new, CAMEL_RECIPIENT_TYPE_BCC, + camel_mime_message_get_recipients (template, CAMEL_RECIPIENT_TYPE_BCC)); + + camel_mime_part_set_content((CamelMimePart *)new, + cont, (int) g_utf8_strlen(cont, -1), "text"); + + /* Create the composer */ + em_utils_edit_message (new, templates_folder); + + camel_object_unref(new); +} + +static void +popup_free (EPopup *ep, GSList *l, void *data) +{ + g_slist_free (l); +} + +static GSList +*append_to_menu (CamelFolder *folder, GPtrArray *uids, GSList *list, EMPopupTargetSelect *t) +{ + int i; + + for (i = 0; i < uids->len; i++) { + const char *subject; + char *path; + EPopupItem *item; + CamelMimeMessage *message; + const char *uid; + + uid = g_strdup (g_ptr_array_index (uids, i)); + + /* Same as in fill_submenu */ + if (!g_str_has_suffix (folder->name, "Templates")) + path = g_strdup_printf ("80.%s", folder->full_name); + else + path = "80.Templates"; + + /* If this uid is trashed, ignore it */ + if (camel_folder_get_message_flags (folder, uid) & CAMEL_MESSAGE_DELETED) + continue; + + /* Get the message for this uid */ + message = camel_folder_get_message (folder, + uid, + NULL); + + subject = camel_mime_message_get_subject (message); + + /* Create the menu item for it */ + item = g_slice_alloc0(sizeof(*item)); + item->type = E_POPUP_ITEM; + item->path = g_strdup_printf ("%s/%02d", path, i); + item->label = g_strdup ((strlen(subject) > 0) ? subject : _("No title")); + item->visible = EM_POPUP_SELECT_MANY | EM_POPUP_SELECT_ONE; + + /* Make some info available to the callback */ + UserData *user_data; + user_data = g_slice_new(UserData); + user_data->msg = message; + user_data->t = t; + + item->user_data = user_data; + item->activate = reply_with_template; + + list = g_slist_prepend (list, item); + } + + return list; +} + +static GSList +*fill_submenu (CamelStore *store, CamelFolderInfo *info, GSList *list, EMPopupTargetSelect *t) +{ + while (info) { + CamelFolder *folder; + GPtrArray *uids; + EPopupItem *item; + + folder = camel_store_get_folder (store, info->full_name, 0, NULL); + + item = g_slice_alloc0(sizeof(*item)); + item->type = E_POPUP_SUBMENU; + item->label = folder->name; + item->visible = EM_POPUP_SELECT_MANY | EM_POPUP_SELECT_ONE; + + /* To avoid having a Templates dir, we ignore the top level */ + if (!g_str_has_suffix (folder->name, "Templates")) + item->path = g_strdup_printf ("80.%s", folder->full_name); + else + item->path = "80.Templates"; + + list = g_slist_prepend (list, item); + + /* Get the uids for this folder and fill them in the menu */ + uids = camel_folder_get_uids (folder); + list = append_to_menu (folder, uids, list, t); + camel_folder_free_uids (folder, uids); + + /* If the folder has a child, call this function again */ + if (info->child) { + list = fill_submenu (store, info->child, list, t); + } + + info = info->next; + } + + return list; +} + +void +org_gnome_templates_popup (EPlugin *ep, EMPopupTargetSelect *t) +{ + CamelFolder *templates_folder; + CamelFolderInfo *templates_info; + CamelStore *store; + char *basedir; + char *url; + + GSList *list = NULL; + + /* We get the templates folder and all the uids of the messages in there */ + basedir = g_build_filename (g_get_home_dir (), ".evolution", "mail", "local", NULL); + url = g_strdup_printf ("mbox://%s", basedir); + + g_free (basedir); + + store = (CamelStore *) camel_session_get_service (session, url, CAMEL_PROVIDER_STORE, NULL); + g_free (url); + + templates_folder = camel_store_get_folder (store, _("Templates"), CAMEL_STORE_FOLDER_CREATE, NULL); + + templates_info = camel_store_get_folder_info (store, + templates_folder->full_name, + CAMEL_STORE_FOLDER_INFO_RECURSIVE | CAMEL_STORE_FOLDER_INFO_FAST, + NULL); + + /* Get subfolders and fill it */ + list = fill_submenu (store, templates_info, list, t); + + e_popup_add_items (t->target.popup, list, NULL, popup_free, NULL); + + return; +} + +static void +action_template_cb (GtkAction *action, + EMsgComposer *composer) +{ + CamelMessageInfo *info; + CamelMimeMessage *msg; + CamelStore *store; + CamelFolder *templates_folder; + + char *basedir; + char *url; + + /* We get the templates folder and all the uids of the messages in there */ + basedir = g_build_filename (g_get_home_dir (), ".evolution", "mail", "local", NULL); + url = g_strdup_printf ("mbox://%s", basedir); + g_free (basedir); + + store = (CamelStore *) camel_session_get_service (session, url, CAMEL_PROVIDER_STORE, NULL); + g_free (url); + + templates_folder = camel_store_get_folder (store, _("Templates"), CAMEL_STORE_FOLDER_CREATE, NULL); + + msg = e_msg_composer_get_message_draft (composer); + info = camel_message_info_new (NULL); + + /* FIXME: what's the ~0 for? :) */ + camel_message_info_set_flags (info, CAMEL_MESSAGE_SEEN | CAMEL_MESSAGE_DRAFT, ~0); + + mail_append_mail (templates_folder, msg, info, NULL, composer); + + return; +} + +static GtkActionEntry entries[] = { + + { "Template", + GTK_STOCK_SAVE, + N_("Save as _Template"), + "<Shift><Control>t", + N_("Save as Template"), + G_CALLBACK (action_template_cb) } +}; + +gboolean +e_plugin_ui_init (GtkUIManager *manager, + EMsgComposer *composer) +{ + GtkhtmlEditor *editor; + + editor = GTKHTML_EDITOR (composer); + + /* Add actions to the "composer" action group. */ + gtk_action_group_add_actions ( + gtkhtml_editor_get_action_group (editor, "composer"), + entries, G_N_ELEMENTS (entries), composer); + + return TRUE; +} diff --git a/plugins/templates/templates.glade b/plugins/templates/templates.glade new file mode 100644 index 0000000000..60720ef3e0 --- /dev/null +++ b/plugins/templates/templates.glade @@ -0,0 +1,126 @@ +<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*--> +<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd"> + +<glade-interface> + +<widget class="GtkWindow" id="window1"> + <property name="visible">True</property> + <property name="title">window1</property> + <property name="type">GTK_WINDOW_TOPLEVEL</property> + <property name="window_position">GTK_WIN_POS_NONE</property> + <property name="modal">False</property> + <property name="resizable">True</property> + <property name="destroy_with_parent">False</property> + <property name="decorated">True</property> + <property name="skip_taskbar_hint">False</property> + <property name="skip_pager_hint">False</property> + <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property> + <property name="gravity">GDK_GRAVITY_NORTH_WEST</property> + <property name="focus_on_map">True</property> + <property name="urgency_hint">False</property> + + <child> + <widget class="GtkVBox" id="templates_configuration_box"> + <property name="width_request">385</property> + <property name="height_request">189</property> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">5</property> + + <child> + <widget class="GtkHBox" id="clue_container"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">6</property> + + <child> + <widget class="GtkScrolledWindow" id="scrolledwindow1"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="shadow_type">GTK_SHADOW_NONE</property> + <property name="window_placement">GTK_CORNER_TOP_LEFT</property> + + <child> + <widget class="GtkTreeView" id="clue_treeview"> + <property name="border_width">1</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="headers_visible">True</property> + <property name="rules_hint">False</property> + <property name="reorderable">False</property> + <property name="enable_search">True</property> + <property name="fixed_height_mode">False</property> + <property name="hover_selection">False</property> + <property name="hover_expand">False</property> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkVButtonBox" id="vbuttonbox2"> + <property name="visible">True</property> + <property name="layout_style">GTK_BUTTONBOX_START</property> + <property name="spacing">6</property> + + <child> + <widget class="GtkButton" id="clue_add"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-add</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + </widget> + </child> + + <child> + <widget class="GtkButton" id="clue_edit"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-edit</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + </widget> + </child> + + <child> + <widget class="GtkButton" id="clue_remove"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-remove</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + </child> +</widget> + +</glade-interface> diff --git a/plugins/webdav-account-setup/Makefile.am b/plugins/webdav-account-setup/Makefile.am new file mode 100644 index 0000000000..ea2ea5fdcb --- /dev/null +++ b/plugins/webdav-account-setup/Makefile.am @@ -0,0 +1,22 @@ +INCLUDES = \ + $(EVOLUTION_ADDRESSBOOK_CFLAGS) \ + -I . \ + -I$(top_srcdir) + +@EVO_PLUGIN_RULE@ + +plugin_DATA = org-gnome-evolution-webdav.eplug +plugin_LTLIBRARIES = liborg-gnome-evolution-webdav.la + +liborg_gnome_evolution_webdav_la_SOURCES = \ + webdav-contacts-source.c + +liborg_gnome_evolution_webdav_la_LIBADD = \ + $(EPLUGIN_LIBS) + +liborg_gnome_evolution_webdav_la_LDFLAGS = -module -avoid-version $(NO_UNDEFINED) + +EXTRA_DIST = \ + org-gnome-evolution-webdav.eplug.xml + +CLEANFILES = org-gnome-evolution-webdav.eplug diff --git a/plugins/webdav-account-setup/org-gnome-evolution-webdav.eplug.xml b/plugins/webdav-account-setup/org-gnome-evolution-webdav.eplug.xml new file mode 100644 index 0000000000..6699a4ae8e --- /dev/null +++ b/plugins/webdav-account-setup/org-gnome-evolution-webdav.eplug.xml @@ -0,0 +1,22 @@ +<?xml version="1.0"?> +<e-plugin-list> + <e-plugin id="org.gnome.evolution.webdav" + type="shlib" + _name="WebDAV contacts" + location="@PLUGINDIR@/liborg-gnome-evolution-webdav@SOEXT@" + load-on-startup="false" + localedir = "@LOCALEDIR@"> + <author name="Matthias Braun" + email="matze@braunis.de"/> + <_description>A plugin to setup WebDAV contacts.</_description> + + <hook class="org.gnome.evolution.addressbook.config:1.0"> + <group target="source" + id="com.novell.evolution.addressbook.config.accountEditor"> + <item type="item" + path="00.general/10.display/00.WebDAV" + factory="plugin_webdav_contacts"/> + </group> + </hook> + </e-plugin> +</e-plugin-list> diff --git a/plugins/webdav-account-setup/webdav-contacts-source.c b/plugins/webdav-account-setup/webdav-contacts-source.c new file mode 100644 index 0000000000..8f944de5ed --- /dev/null +++ b/plugins/webdav-account-setup/webdav-contacts-source.c @@ -0,0 +1,370 @@ +/* + * + * Copyright (C) 2008 Matthias Braun <matze@braunis.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU Lesser General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <string.h> + +#include <glib/gi18n-lib.h> +#include <glib.h> + +#include <gtk/gtk.h> + +#include <e-util/e-config.h> +#include <e-util/e-plugin.h> +#include <addressbook/gui/widgets/eab-config.h> + +#include <libedataserver/e-source.h> +#include <libedataserver/e-source-list.h> +#include <libedataserver/e-url.h> +#include <libedataserver/e-account-list.h> + +#define BASE_URI "webdav://" + +typedef struct { + ESource *source; + GtkWidget *box; + GtkEntry *url_entry; + GtkEntry *username_entry; + GtkToggleButton *avoid_ifmatch_toggle; +} ui_data; + +GtkWidget * +plugin_webdav_contacts(EPlugin *epl, EConfigHookItemFactoryData *data); + +int +e_plugin_lib_enable(EPluginLib *ep, int enable); + +static void +ensure_webdav_contacts_source_group(void) +{ + ESourceList *source_list; + ESourceGroup *group; + + source_list = e_source_list_new_for_gconf_default("/apps/evolution/addressbook/sources"); + + if (source_list == NULL) { + return; + } + + group = e_source_list_peek_group_by_name(source_list, _("WebDAV")); + + if (group == NULL) { + gboolean res; + + group = e_source_group_new(_("WebDAV"), BASE_URI); + res = e_source_list_add_group(source_list, group, -1); + + if (res == FALSE) { + g_warning("Could not add WebDAV source group!"); + } else { + e_source_list_sync(source_list, NULL); + } + + g_object_unref(group); + } + g_object_unref(source_list); +} + +static void +remove_webdav_contacts_source_group(void) +{ + ESourceList *source_list; + ESourceGroup *group; + + source_list = e_source_list_new_for_gconf_default("/apps/evolution/addressbook/sources"); + + if (source_list == NULL) { + return; + } + + group = e_source_list_peek_group_by_name(source_list, _("WebDAV")); + + if (group) { + GSList *sources; + + sources = e_source_group_peek_sources(group); + + if (NULL == sources) { + e_source_list_remove_group(source_list, group); + e_source_list_sync(source_list, NULL); + } + } + g_object_unref(source_list); +} + +/* stolen from caldav plugin which stole it from calendar-weather eplugin */ +static gchar * +print_uri_noproto(EUri *uri) +{ + gchar *uri_noproto; + + if (uri->port != 0) + uri_noproto = g_strdup_printf( + "%s%s%s%s%s%s%s:%d%s%s%s", + uri->user ? uri->user : "", + uri->authmech ? ";auth=" : "", + uri->authmech ? uri->authmech : "", + uri->passwd ? ":" : "", + uri->passwd ? uri->passwd : "", + uri->user ? "@" : "", + uri->host ? uri->host : "", + uri->port, + uri->path ? uri->path : "", + uri->query ? "?" : "", + uri->query ? uri->query : ""); + else + uri_noproto = g_strdup_printf( + "%s%s%s%s%s%s%s%s%s%s", + uri->user ? uri->user : "", + uri->authmech ? ";auth=" : "", + uri->authmech ? uri->authmech : "", + uri->passwd ? ":" : "", + uri->passwd ? uri->passwd : "", + uri->user ? "@" : "", + uri->host ? uri->host : "", + uri->path ? uri->path : "", + uri->query ? "?" : "", + uri->query ? uri->query : ""); + return uri_noproto; +} + +static void +set_ui_from_source(ui_data *data) +{ + ESource *source = data->source; + const char *url = e_source_get_uri(source); + EUri *uri = e_uri_new(url); + char *url_ui; + const char *property; + gboolean use_ssl; + gboolean avoid_ifmatch; + + property = e_source_get_property(source, "use_ssl"); + if (property != NULL && strcmp(property, "1") == 0) { + use_ssl = TRUE; + } else { + use_ssl = FALSE; + } + + property = e_source_get_property(source, "avoid_ifmatch"); + if (property != NULL && strcmp(property, "1") == 0) { + avoid_ifmatch = TRUE; + } else { + avoid_ifmatch = FALSE; + } + gtk_toggle_button_set_active(data->avoid_ifmatch_toggle, avoid_ifmatch); + + /* it's really a http or https protocol */ + g_free(uri->protocol); + uri->protocol = g_strdup(use_ssl ? "https" : "http"); + + /* remove user/username and set user field */ + if (uri->user != NULL) { + gtk_entry_set_text(data->username_entry, uri->user); + g_free(uri->user); + uri->user = NULL; + } else { + gtk_entry_set_text(data->username_entry, ""); + } + + url_ui = e_uri_to_string(uri, TRUE); + gtk_entry_set_text(data->url_entry, url_ui); + + g_free(url_ui); + e_uri_free(uri); +} + +static void +set_source_from_ui(ui_data *data) +{ + ESource *source = data->source; + gboolean avoid_ifmatch = gtk_toggle_button_get_active(data->avoid_ifmatch_toggle); + const char *url = gtk_entry_get_text(data->url_entry); + EUri *uri = e_uri_new(url); + char *url_noprotocol; + gboolean use_ssl; + + e_source_set_property(source, "avoid_ifmatch", avoid_ifmatch ? "1" : "0"); + + /* put username into uri */ + g_free(uri->user); + uri->user = g_strdup(gtk_entry_get_text(data->username_entry)); + + if (uri->user[0] != '\0') { + e_source_set_property(source, "auth", "plain/password"); + e_source_set_property(source, "username", uri->user); + } else { + e_source_set_property(source, "auth", NULL); + e_source_set_property(source, "username", NULL); + } + + /* set use_ssl based on protocol in URL */ + if (strcmp(uri->protocol, "https") == 0) { + use_ssl = TRUE; + } else { + use_ssl = FALSE; + } + e_source_set_property(source, "use_ssl", use_ssl ? "1" : "0"); + + url_noprotocol = print_uri_noproto(uri); + e_source_set_relative_uri(source, url_noprotocol); + g_free(url_noprotocol); + e_uri_free(uri); +} + +static void +on_entry_changed(GtkEntry *entry, gpointer user_data) +{ + (void) entry; + set_source_from_ui(user_data); +} + +static void +on_toggle_changed(GtkToggleButton *tb, gpointer user_data) +{ + (void) tb; + set_source_from_ui(user_data); +} + +static void +destroy_ui_data(gpointer data) +{ + ui_data *ui_data = data; + + gtk_widget_destroy(ui_data->box); + g_free(ui_data); +} + +GtkWidget * +plugin_webdav_contacts(EPlugin *epl, EConfigHookItemFactoryData *data) +{ + EABConfigTargetSource *t = (EABConfigTargetSource *) data->target; + ESource *source; + ESourceGroup *group; + const char *base_uri; + GtkWidget *parent; + GtkWidget *vbox; + + GtkWidget *section; + GtkWidget *vbox2; + + GtkBox *hbox; + GtkWidget *spacer; + GtkWidget *label; + + ui_data *uidata; + + source = t->source; + group = e_source_peek_group (source); + + base_uri = e_source_group_peek_base_uri (group); + + g_object_set_data_full (G_OBJECT (epl), "widget", NULL, + (GDestroyNotify)gtk_widget_destroy); + + if (strcmp(base_uri, BASE_URI) != 0) { + return NULL; + } + + uidata = g_malloc0(sizeof(uidata[0])); + uidata->source = source; + + /* Build up the UI */ + parent = data->parent; + vbox = gtk_widget_get_ancestor(gtk_widget_get_parent(parent), GTK_TYPE_VBOX); + + vbox2 = gtk_vbox_new(FALSE, 6); + gtk_box_pack_start(GTK_BOX(vbox), vbox2, FALSE, FALSE, 0); + + section = gtk_label_new(NULL); + gtk_label_set_markup(GTK_LABEL(section), _("<b>Server</b>")); + gtk_misc_set_alignment(GTK_MISC(section), 0.0, 0.0); + gtk_box_pack_start(GTK_BOX(vbox2), section, FALSE, FALSE, 0); + + + hbox = GTK_BOX(gtk_hbox_new(FALSE, 10)); + gtk_box_pack_start(GTK_BOX(vbox2), GTK_WIDGET(hbox), TRUE, TRUE, 0); + + spacer = gtk_label_new(" "); + gtk_box_pack_start(hbox, spacer, FALSE, FALSE, 0); + + label = gtk_label_new(_("URL:")); + gtk_box_pack_start(hbox, label, FALSE, FALSE, 0); + + uidata->url_entry = GTK_ENTRY(gtk_entry_new()); + gtk_box_pack_start(hbox, GTK_WIDGET(uidata->url_entry), TRUE, TRUE, 0); + + hbox = GTK_BOX(gtk_hbox_new(FALSE, 10)); + gtk_box_pack_start(GTK_BOX(vbox2), GTK_WIDGET(hbox), TRUE, TRUE, 0); + + spacer = gtk_label_new(" "); + gtk_box_pack_start(hbox, spacer, FALSE, FALSE, 0); + + label = gtk_label_new_with_mnemonic(_("User_name:")); + gtk_box_pack_start(hbox, label, FALSE, FALSE, 0); + + uidata->username_entry = GTK_ENTRY(gtk_entry_new()); + gtk_box_pack_start(hbox, GTK_WIDGET(uidata->username_entry), TRUE, TRUE, 0); + + + hbox = GTK_BOX(gtk_hbox_new(FALSE, 10)); + gtk_box_pack_start(GTK_BOX(vbox2), GTK_WIDGET(hbox), TRUE, TRUE, 0); + + spacer = gtk_label_new(" "); + gtk_box_pack_start(hbox, spacer, FALSE, FALSE, 0); + + uidata->avoid_ifmatch_toggle = GTK_TOGGLE_BUTTON( + gtk_check_button_new_with_mnemonic( + _("_Avoid IfMatch (needed on apache < 2.2.8)"))); + gtk_box_pack_start(hbox, GTK_WIDGET(uidata->avoid_ifmatch_toggle), + FALSE, FALSE, 0); + + set_ui_from_source(uidata); + + gtk_widget_show_all(vbox2); + + uidata->box = vbox2; + g_object_set_data_full(G_OBJECT(epl), "widget", uidata, + destroy_ui_data); + + g_signal_connect(G_OBJECT(uidata->username_entry), "changed", + G_CALLBACK(on_entry_changed), uidata); + g_signal_connect(G_OBJECT(uidata->url_entry), "changed", + G_CALLBACK(on_entry_changed), uidata); + g_signal_connect(G_OBJECT(uidata->avoid_ifmatch_toggle), "toggled", + G_CALLBACK(on_toggle_changed), uidata); + + return NULL; +} + +int +e_plugin_lib_enable(EPluginLib *ep, int enable) +{ + if (enable) { + ensure_webdav_contacts_source_group(); + } else { + remove_webdav_contacts_source_group(); + } + return 0; +} + |