2007-11-27 [wwp] 3.1.0cvs33
authorTristan Chabredier <wwp@claws-mail.org>
Tue, 27 Nov 2007 09:37:23 +0000 (09:37 +0000)
committerTristan Chabredier <wwp@claws-mail.org>
Tue, 27 Nov 2007 09:37:23 +0000 (09:37 +0000)
* src/Makefile.am
* src/addrcustomattr.c
* src/addrcustomattr.h
* src/addressbook.c
* src/editaddress.c
* src/editaddress.h
* src/prefs_common.c
* src/prefs_common.h
* src/common/defs.h
Provide a (customizable) list of preset attribute names
for editing contacts (closes bug #1391).

12 files changed:
ChangeLog
PATCHSETS
configure.ac
src/Makefile.am
src/addrcustomattr.c [new file with mode: 0644]
src/addrcustomattr.h [new file with mode: 0644]
src/addressbook.c
src/common/defs.h
src/editaddress.c
src/editaddress.h
src/prefs_common.c
src/prefs_common.h

index 8db8071..ca8dc25 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,17 @@
+2007-11-27 [wwp]       3.1.0cvs33
+
+       * src/Makefile.am
+       * src/addrcustomattr.c
+       * src/addrcustomattr.h
+       * src/addressbook.c
+       * src/editaddress.c
+       * src/editaddress.h
+       * src/prefs_common.c
+       * src/prefs_common.h
+       * src/common/defs.h
+               Provide a (customizable) list of preset attribute names
+               for editing contacts (closes bug #1391).
+
 2007-11-27 [wwp]       3.1.0cvs32
 
        * src/addrclip.c
index 2f7e24f..3c7e0f6 100644 (file)
--- a/PATCHSETS
+++ b/PATCHSETS
 ( cvs diff -u -r 1.1.2.13 -r 1.1.2.14 src/edittags.c;  cvs diff -u -r 1.213.2.167 -r 1.213.2.168 src/folder.c;  cvs diff -u -r 1.87.2.50 -r 1.87.2.51 src/folder.h;  cvs diff -u -r 1.179.2.199 -r 1.179.2.200 src/imap.c;  cvs diff -u -r 1.94.2.165 -r 1.94.2.166 src/messageview.c;  cvs diff -u -r 1.150.2.105 -r 1.150.2.106 src/procmsg.c;  cvs diff -u -r 1.60.2.50 -r 1.60.2.51 src/procmsg.h;  cvs diff -u -r 1.395.2.339 -r 1.395.2.340 src/summaryview.c;  cvs diff -u -r 1.36.2.126 -r 1.36.2.127 src/common/utils.c;  cvs diff -u -r 1.20.2.55 -r 1.20.2.56 src/common/utils.h;  cvs diff -u -r 1.1.4.88 -r 1.1.4.89 src/etpan/imap-thread.c;  cvs diff -u -r 1.1.4.20 -r 1.1.4.21 src/etpan/imap-thread.h;  ) > 3.1.0cvs30.patchset
 ( cvs diff -u -r 1.213.2.168 -r 1.213.2.169 src/folder.c;  cvs diff -u -r 1.179.2.200 -r 1.179.2.201 src/imap.c;  ) > 3.1.0cvs31.patchset
 ( cvs diff -u -r 1.5.2.6 -r 1.5.2.7 src/addrclip.c;  cvs diff -u -r 1.60.2.106 -r 1.60.2.107 src/addressbook.c;  cvs diff -u -r 1.13.2.16 -r 1.13.2.17 src/addritem.c;  ) > 3.1.0cvs32.patchset
+( cvs diff -u -r 1.155.2.80 -r 1.155.2.81 src/Makefile.am;  diff -u /dev/null src/addrcustomattr.c;  diff -u /dev/null src/addrcustomattr.h;  cvs diff -u -r 1.60.2.107 -r 1.60.2.108 src/addressbook.c;  cvs diff -u -r 1.14.2.45 -r 1.14.2.46 src/editaddress.c;  cvs diff -u -r 1.3.2.7 -r 1.3.2.8 src/editaddress.h;  cvs diff -u -r 1.204.2.155 -r 1.204.2.156 src/prefs_common.c;  cvs diff -u -r 1.103.2.100 -r 1.103.2.101 src/prefs_common.h;  cvs diff -u -r 1.9.2.43 -r 1.9.2.44 src/common/defs.h;  ) > 3.1.0cvs33.patchset
index bbf9113..e70b700 100644 (file)
@@ -11,7 +11,7 @@ MINOR_VERSION=1
 MICRO_VERSION=0
 INTERFACE_AGE=0
 BINARY_AGE=0
-EXTRA_VERSION=32
+EXTRA_VERSION=33
 EXTRA_RELEASE=
 EXTRA_GTK2_VERSION=
 
index e580ef2..c2a522c 100644 (file)
@@ -21,6 +21,7 @@ claws_mail_SOURCES = \
        addrcache.c \
        addrclip.c \
        addr_compl.c \
+       addrcustomattr.c \
        addressadd.c \
        addressbook.c \
        addressbook_foldersel.c \
@@ -167,6 +168,7 @@ claws_mailinclude_HEADERS = \
        addrclip.h \
        addr_compl.h \
        addrdefs.h \
+       addrcustomattr.h \
        addressadd.h \
        addressbook.h \
        addressbook_foldersel.h \
diff --git a/src/addrcustomattr.c b/src/addrcustomattr.c
new file mode 100644 (file)
index 0000000..df526b3
--- /dev/null
@@ -0,0 +1,640 @@
+/*
+ * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 2007 The Claws Mail Team
+ *
+ * This program 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * 
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include "defs.h"
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "menu.h"
+#include "addrcustomattr.h"
+#include "manage_window.h"
+#include "prefs_common.h"
+#include "alertpanel.h"
+#include "addrbook.h"
+#include "editaddress.h"
+
+static GtkItemFactory *custom_attr_popup_factory = NULL;
+static GtkWidget *custom_attr_popup_menu = NULL;
+
+static struct CustomAttrWindow
+{
+       GtkWidget *window;
+       GtkWidget *attr_list;
+       GtkWidget *hbox1;
+       GtkWidget *hbox2;
+       GtkWidget *vbox1;
+       GtkWidget *label;
+       GtkWidget *cancel_btn;
+       GtkWidget *ok_btn;
+       GtkWidget *add_entry;
+       GtkWidget *add_btn;
+} custom_attr_window;
+
+enum {
+       CUSTOM_ATTR_NAME,
+       N_CUSTOM_ATTR
+};
+
+static gchar *default_addressbook_attributes_table[] = {
+       N_("birth date"),
+       N_("home address"),
+       N_("home phone"),
+       N_("home mobile phone"),
+       N_("website"),
+       NULL
+};
+
+static gboolean dirty = FALSE;
+
+static void custom_attr_window_create(void);
+static void custom_attr_selected_attr_edited(GtkCellRendererText *widget,
+               gchar *arg1, gchar *arg2,
+               GtkWidget *list_view);
+static void custom_attr_window_load_list(GList *list);
+static void custom_attr_window_save_list (void);
+static GList *custom_attr_default_list(void);
+
+void addressbook_custom_attr_edit()
+{
+       if (!custom_attr_window.window)
+               custom_attr_window_create();
+
+       manage_window_set_transient(GTK_WINDOW(custom_attr_window.window));
+       gtk_widget_grab_focus(custom_attr_window.ok_btn);
+       
+       custom_attr_window_load_list(prefs_common.addressbook_custom_attributes);
+
+       gtk_widget_show(custom_attr_window.window);
+       gtk_widget_grab_focus(custom_attr_window.attr_list);
+       gtk_window_set_modal(GTK_WINDOW(custom_attr_window.window), TRUE);
+}
+
+static gint custom_attr_cmp_func (GtkTreeModel *model, GtkTreeIter *a,
+                                                                 GtkTreeIter *b, gpointer userdata)
+ {
+       gchar *name1, *name2;
+
+       gtk_tree_model_get(model, a, CUSTOM_ATTR_NAME, &name1, -1);
+       gtk_tree_model_get(model, b, CUSTOM_ATTR_NAME, &name2, -1);
+       
+       if (name1 == NULL)
+               return name2 == NULL ? 0:1;
+       
+       if (name2 == NULL)
+               return name1 == NULL ? 0:1;
+       
+       return g_utf8_collate(name1, name2);
+}
+
+static GtkListStore* custom_attr_window_create_data_store(void)
+{
+       GtkListStore *store = gtk_list_store_new(N_CUSTOM_ATTR,
+                                 G_TYPE_STRING,
+                                 -1);
+       GtkTreeSortable *sortable = GTK_TREE_SORTABLE(store);
+
+       gtk_tree_sortable_set_sort_func(sortable, 0, custom_attr_cmp_func,
+                                    NULL, NULL);
+
+       return store;
+}
+
+static void custom_attr_window_create_list_view_columns(GtkWidget *list_view)
+{
+       GtkTreeViewColumn *column;
+       GtkCellRenderer *renderer;
+
+       renderer = gtk_cell_renderer_text_new();
+       g_object_set(G_OBJECT(renderer), "editable", TRUE, NULL);
+
+       column = gtk_tree_view_column_new_with_attributes
+               (_("Attribute name"),
+                renderer,
+                "text", CUSTOM_ATTR_NAME,
+                NULL);
+       gtk_tree_view_append_column(GTK_TREE_VIEW(list_view), column);
+       gtk_tree_view_column_set_resizable(column, TRUE);
+       gtk_tree_view_set_search_column(GTK_TREE_VIEW(list_view),
+                                       CUSTOM_ATTR_NAME);
+       g_signal_connect(G_OBJECT(renderer), "edited",
+                        G_CALLBACK(custom_attr_selected_attr_edited),
+                        list_view);
+}
+
+static void custom_attr_window_list_view_clear_list(GtkWidget *list_view, gboolean warn)
+{
+       if (!warn || alertpanel(_("Clear attribute names list"),
+                      _("Do you really want to delete all attribute names?"),
+                      GTK_STOCK_CANCEL, GTK_STOCK_DELETE, NULL) == G_ALERTALTERNATE) {
+               GtkListStore *list_store = GTK_LIST_STORE(gtk_tree_view_get_model
+                                               (GTK_TREE_VIEW(list_view)));
+               gtk_list_store_clear(list_store);
+               dirty = TRUE;
+       }
+}
+
+static void custom_attr_popup_clear_list (void *obj, guint action, void *data)
+{
+       custom_attr_window_list_view_clear_list(custom_attr_window.attr_list, TRUE);
+}
+
+static void custom_attr_popup_delete (void *obj, guint action, void *data)
+{
+       GtkTreeIter sel;
+       GtkTreeModel *model;
+       
+       if (!gtk_tree_selection_get_selected(gtk_tree_view_get_selection
+                               (GTK_TREE_VIEW(custom_attr_window.attr_list)),
+                               &model, &sel))
+               return;                         
+
+       if (alertpanel(_("Delete attribute name"),
+                      _("Do you really want to delete this attribute name?"),
+                      GTK_STOCK_CANCEL, GTK_STOCK_DELETE, NULL) == G_ALERTALTERNATE) {
+               gtk_list_store_remove(GTK_LIST_STORE(model), &sel);
+               dirty = TRUE;
+       }
+}
+
+static void custom_attr_popup_factory_defaults (void *obj, guint action, void *data)
+{
+       if (alertpanel(_("Reset to factory defaults"),
+                      _("Do you really want to replace all attribute names\nwith the default ones?"),
+                      GTK_STOCK_CANCEL, GTK_STOCK_APPLY, NULL) == G_ALERTALTERNATE) {
+               GList *tmp = custom_attr_default_list();
+               custom_attr_window_load_list(tmp);
+               if (tmp) {
+                       GList *cur;
+                       cur = tmp;
+                       while (cur) {
+                               g_free(cur->data);
+                               cur = cur->next;
+                       }
+                       g_list_free(tmp);
+               }
+               dirty = TRUE;
+       }
+}
+
+static GtkItemFactoryEntry custom_attr_popup_entries[] =
+{
+       {N_("/_Clear list"),                                    NULL, custom_attr_popup_clear_list, 0, NULL, NULL},
+       {N_("/_Delete"),                                                NULL, custom_attr_popup_delete, 0, NULL, NULL},
+       {N_("/_Reset to factory defaults"),             NULL, custom_attr_popup_factory_defaults, 0, NULL, NULL},
+};
+
+static gint custom_attr_list_btn_pressed(GtkWidget *widget, GdkEventButton *event,
+                                   GtkTreeView *list_view)
+{
+       if (event && event->button == 3) {
+               if (!custom_attr_popup_menu) {
+                       gint n_entries = sizeof(custom_attr_popup_entries) /
+                                       sizeof(custom_attr_popup_entries[0]);
+                       custom_attr_popup_menu = menu_create_items(custom_attr_popup_entries,
+                                                         n_entries, "<CustomAttrPopupMenu>",
+                                                         &custom_attr_popup_factory, list_view);
+               }
+               gtk_menu_popup(GTK_MENU(custom_attr_popup_menu), 
+                              NULL, NULL, NULL, NULL, 
+                              event->button, event->time);
+
+               return FALSE;
+       }
+       return FALSE;
+}
+
+static gboolean custom_attr_list_popup_menu(GtkWidget *widget, gpointer data)
+{
+       GtkTreeView *list_view = (GtkTreeView *)data;
+       GdkEventButton event;
+       
+       event.button = 3;
+       event.time = gtk_get_current_event_time();
+       
+       custom_attr_list_btn_pressed(NULL, &event, list_view);
+
+       return TRUE;
+}
+
+static GtkWidget *custom_attr_window_list_view_create  (void)
+{
+       GtkTreeView *list_view;
+       GtkTreeSelection *selector;
+       GtkTreeModel *model;
+
+       model = GTK_TREE_MODEL(custom_attr_window_create_data_store());
+       list_view = GTK_TREE_VIEW(gtk_tree_view_new_with_model(model));
+       g_object_unref(model);  
+       gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(model),
+                       CUSTOM_ATTR_NAME, GTK_SORT_ASCENDING);
+
+       gtk_tree_view_set_rules_hint(list_view, prefs_common.use_stripes_everywhere);
+       
+       selector = gtk_tree_view_get_selection(list_view);
+       gtk_tree_selection_set_mode(selector, GTK_SELECTION_BROWSE);
+
+       /* create the columns */
+       custom_attr_window_create_list_view_columns(GTK_WIDGET(list_view));
+
+#ifndef MAEMO
+       g_signal_connect(G_OBJECT(list_view), "popup-menu",
+                        G_CALLBACK(custom_attr_list_popup_menu), list_view);
+#else
+       gtk_widget_tap_and_hold_setup(GTK_WIDGET(list_view), NULL, NULL,
+                       GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
+       g_signal_connect(G_OBJECT(list_view), "tap-and-hold",
+                        G_CALLBACK(custom_attr_list_popup_menu), list_view);
+#endif
+       g_signal_connect(G_OBJECT(list_view), "button-press-event",
+                       G_CALLBACK(custom_attr_list_btn_pressed), list_view);
+       return GTK_WIDGET(list_view);
+}
+
+static void custom_attr_window_close(void) 
+{
+       if (dirty)
+               custom_attr_window_save_list();
+       gtk_widget_hide(custom_attr_window.window);
+       if (dirty && !prefs_common.addressbook_use_editaddress_dialog)
+               addressbook_edit_reload_attr_list();
+}
+
+static void custom_attr_window_cancel_cb(GtkWidget *widget,
+                                gpointer data) 
+{
+       dirty = FALSE;
+       custom_attr_window_close();
+}
+
+static void custom_attr_window_ok_cb(GtkWidget *widget,
+                                gpointer data) 
+{
+       custom_attr_window_close();
+}
+
+static void custom_attr_selected_attr_edited(GtkCellRendererText *widget,
+               gchar *path, gchar *new_text,
+               GtkWidget *list_view)
+{
+       GtkTreeIter iter;
+       GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(list_view));
+       SummaryView *summaryview = NULL;
+
+       if (mainwindow_get_mainwindow() != NULL)
+               summaryview = mainwindow_get_mainwindow()->summaryview;
+
+       if (!gtk_tree_model_get_iter_from_string(model, &iter, path))
+               return;
+
+       if (!new_text || !*new_text)
+               return;
+
+       gtk_list_store_set(GTK_LIST_STORE(model), &iter,
+                          CUSTOM_ATTR_NAME, new_text,
+                          -1);
+       dirty = TRUE;
+}
+
+typedef struct FindAttrInStore {
+       gchar           *attr;
+       GtkTreePath     *path;
+       GtkTreeIter      iter;
+} FindAttrInStore;
+
+static gboolean find_attr_in_store(GtkTreeModel *model,
+                                     GtkTreePath  *path,
+                                     GtkTreeIter  *iter,
+                                     FindAttrInStore *data)
+{
+       gchar *attr;
+       gtk_tree_model_get(model, iter, CUSTOM_ATTR_NAME, &attr, -1);
+
+       if (g_utf8_collate(data->attr, attr)==0) {
+               data->path = path; /* signal we found it */
+               data->iter = *iter;
+               return TRUE;
+       }
+       return FALSE; 
+}
+
+static void custom_attr_window_add_attr(void)
+{
+       gchar *new_attr = gtk_editable_get_chars(GTK_EDITABLE(custom_attr_window.add_entry),
+                                                               0, -1);
+       g_strstrip(new_attr);
+       if (new_attr && *new_attr) {
+               GtkListStore *list_store = GTK_LIST_STORE(gtk_tree_view_get_model
+                                               (GTK_TREE_VIEW(custom_attr_window.attr_list)));
+               FindAttrInStore fis;
+
+               fis.attr = new_attr;
+               fis.path = NULL;
+               gtk_tree_model_foreach(gtk_tree_view_get_model
+                               (GTK_TREE_VIEW(custom_attr_window.attr_list)), 
+                               (GtkTreeModelForeachFunc) find_attr_in_store,
+                               &fis);
+
+               if (fis.path) {
+                       /* activate existing one */
+                       GtkTreeSelection *selection;
+                       GtkTreePath* path;
+                       GtkTreeModel *model = gtk_tree_view_get_model(
+                               GTK_TREE_VIEW(custom_attr_window.attr_list));
+
+                       selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(custom_attr_window.attr_list));
+                       gtk_tree_selection_select_iter(selection, &fis.iter);
+
+                       path = gtk_tree_model_get_path(model, &fis.iter);
+                       /* XXX returned path may not be valid??? create new one to be sure */ 
+                       gtk_tree_view_set_cursor(GTK_TREE_VIEW(custom_attr_window.attr_list),
+                                               path, NULL, FALSE);
+
+                       gtk_list_store_set(list_store, &fis.iter,
+                                               CUSTOM_ATTR_NAME, new_attr,
+                                               -1);
+
+                       gtk_tree_path_free(path);
+               } else {
+                       /* append new */
+                       GtkTreeIter iter;
+
+                       gtk_list_store_append(list_store, &iter);
+                       gtk_list_store_set(list_store, &iter,
+                                          CUSTOM_ATTR_NAME, new_attr,
+                                          -1);
+               }
+               dirty = TRUE;
+       } else {
+               alertpanel_error(_("Attribute name is not set."));
+       }
+       g_free(new_attr);
+}
+
+static void custom_attr_window_add_attr_cb(GtkWidget *widget,
+                                gpointer data) 
+{
+       custom_attr_window_add_attr();
+       gtk_entry_set_text(GTK_ENTRY(custom_attr_window.add_entry), "");
+       gtk_widget_grab_focus(custom_attr_window.attr_list);
+}
+
+static void custom_attr_window_del_attr_cb(GtkWidget *widget,
+                                gpointer data) 
+{
+       custom_attr_popup_delete(NULL, 0, NULL);
+       gtk_widget_grab_focus(custom_attr_window.attr_list);
+}
+
+static gboolean custom_attr_window_key_pressed(GtkWidget *widget,
+                                                                                          GdkEventKey *event, gpointer data)
+{
+       if (event && event->keyval == GDK_Escape)
+               custom_attr_window_close();
+       else if (event && event->keyval == GDK_Delete)
+               custom_attr_popup_delete(NULL, 0, NULL);
+       return FALSE;
+}
+
+static gboolean custom_attr_window_add_key_pressed(GtkWidget *widget,
+                                                                                                  GdkEventKey *event, gpointer data)
+{
+       if (event && (event->keyval == GDK_KP_Enter || event->keyval == GDK_Return)) {
+               custom_attr_window_add_attr();
+               gtk_entry_set_text(GTK_ENTRY(custom_attr_window.add_entry), "");
+               gtk_widget_grab_focus(custom_attr_window.attr_list);
+       }
+       return FALSE;
+}
+
+static void custom_attr_window_create(void) 
+{
+       GtkWidget *window;
+       GtkWidget *hbox1;
+       GtkWidget *hbox2;
+       GtkWidget *vbox1;
+       GtkWidget *label;
+       GtkWidget *attr_list;
+       GtkWidget *cancel_btn;
+       GtkWidget *ok_btn;
+       GtkWidget *scrolledwin;
+       GtkWidget *new_attr_label;
+       GtkWidget *new_attr_entry;
+       GtkWidget *add_btn;
+       GtkWidget *del_btn;
+
+       window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "custom_attr_edit_window");
+       gtk_window_set_title (GTK_WINDOW(window),
+                             Q_("Dialog title|Edit attribute names"));
+
+       gtk_container_set_border_width (GTK_CONTAINER (window), 8);
+       gtk_window_set_position (GTK_WINDOW (window), GTK_WIN_POS_CENTER);
+       gtk_window_set_resizable(GTK_WINDOW (window), TRUE);
+       g_signal_connect(G_OBJECT(window), "delete_event",
+                        G_CALLBACK(custom_attr_window_cancel_cb), NULL);
+       g_signal_connect(G_OBJECT(window), "key_press_event",
+                        G_CALLBACK(custom_attr_window_key_pressed), NULL);
+       MANAGE_WINDOW_SIGNALS_CONNECT (window);
+
+       vbox1 = gtk_vbox_new(FALSE, 6);
+       hbox1 = gtk_hbox_new(FALSE, 6);
+       
+       new_attr_label = gtk_label_new(_("New attribute name:"));
+       gtk_misc_set_alignment(GTK_MISC(new_attr_label), 0, 0.5);
+       gtk_box_pack_start(GTK_BOX(hbox1), new_attr_label, FALSE, FALSE, 0);
+       
+       new_attr_entry = gtk_entry_new();
+       gtk_box_pack_start(GTK_BOX(hbox1), new_attr_entry, FALSE, FALSE, 0);
+       g_signal_connect(G_OBJECT(new_attr_entry), "key_press_event",
+                        G_CALLBACK(custom_attr_window_add_key_pressed), NULL);
+       
+       add_btn = gtk_button_new_from_stock(GTK_STOCK_ADD);
+       gtk_box_pack_start(GTK_BOX(hbox1), add_btn, FALSE, FALSE, 0);
+       
+       del_btn = gtk_button_new_from_stock(GTK_STOCK_DELETE);
+       gtk_box_pack_start(GTK_BOX(hbox1), del_btn, FALSE, FALSE, 0);
+       
+       gtkut_stock_button_set_create(&hbox2, &cancel_btn, GTK_STOCK_CANCEL,
+                                     &ok_btn, GTK_STOCK_OK,
+                                     NULL, NULL);
+
+       gtk_widget_show(new_attr_label);
+       gtk_widget_show(new_attr_entry);
+       gtk_widget_show(add_btn);
+       gtk_widget_show(del_btn);
+       gtk_widget_show(cancel_btn);
+       gtk_widget_show(ok_btn);
+
+       g_signal_connect(G_OBJECT(cancel_btn), "clicked",
+                        G_CALLBACK(custom_attr_window_cancel_cb), NULL);
+       g_signal_connect(G_OBJECT(ok_btn), "clicked",
+                        G_CALLBACK(custom_attr_window_ok_cb), NULL);
+       g_signal_connect(G_OBJECT(add_btn), "clicked",
+                        G_CALLBACK(custom_attr_window_add_attr_cb), NULL);
+       g_signal_connect(G_OBJECT(del_btn), "clicked",
+                        G_CALLBACK(custom_attr_window_del_attr_cb), NULL);
+
+       attr_list = custom_attr_window_list_view_create();
+       
+       label = gtk_label_new(_("Adding or removing attribute names won't affect attributes already set to contacts."));
+       gtk_widget_set_size_request(GTK_WIDGET(label), 380, -1);
+       gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
+       gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
+       gtk_box_pack_start(GTK_BOX(vbox1), label, FALSE, TRUE, 0);
+       
+       scrolledwin = gtk_scrolled_window_new(NULL, NULL);
+       gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
+                                      GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
+                                      
+       gtk_widget_set_size_request(scrolledwin, 400, 250);
+
+       gtk_container_add(GTK_CONTAINER(scrolledwin), attr_list);
+       gtk_box_pack_start(GTK_BOX(vbox1), scrolledwin, TRUE, TRUE, 0);
+       gtk_box_pack_start(GTK_BOX(vbox1), hbox1, FALSE, FALSE, 0);
+       gtk_box_pack_start(GTK_BOX(vbox1), hbox2, FALSE, FALSE, 0);
+       
+       gtk_widget_show(label);
+       gtk_widget_show(scrolledwin);
+       gtk_widget_show(attr_list);
+       gtk_widget_show(hbox2);
+       gtk_widget_show(hbox1);
+       gtk_widget_show(vbox1);
+       gtk_container_add(GTK_CONTAINER (window), vbox1);
+
+       custom_attr_window.window = window;
+       custom_attr_window.hbox1 = hbox1;
+       custom_attr_window.hbox2 = hbox2;
+       custom_attr_window.vbox1 = vbox1;
+       custom_attr_window.label = label;
+       custom_attr_window.attr_list = attr_list;
+       custom_attr_window.cancel_btn = cancel_btn;
+       custom_attr_window.ok_btn = ok_btn;
+       custom_attr_window.add_btn = add_btn;
+       custom_attr_window.add_entry = new_attr_entry;
+}
+
+static void custom_attr_window_load_list (GList *list) 
+{
+       /* copy attribute names list from prefs to store */
+       GList *cur;
+       GtkTreeIter iter;
+       GtkListStore *list_store = GTK_LIST_STORE(gtk_tree_view_get_model
+                                       (GTK_TREE_VIEW(custom_attr_window.attr_list)));
+
+       custom_attr_window_list_view_clear_list(custom_attr_window.attr_list, FALSE);
+       
+       cur = list;
+       while (cur) {
+               gtk_list_store_append(list_store, &iter);
+               gtk_list_store_set(list_store, &iter,
+                                  CUSTOM_ATTR_NAME, cur->data,
+                                  -1);
+               cur = cur->next;
+       }
+}
+
+static GList *store_to_glist = NULL;
+
+static gboolean custom_attr_store_to_glist (GtkTreeModel *model,
+                                     GtkTreePath  *path,
+                                     GtkTreeIter  *iter,
+                                     gpointer     *data)
+{
+       gchar *attr;
+
+       gtk_tree_model_get(model, iter, CUSTOM_ATTR_NAME, &attr, -1);
+       if (attr) {
+               store_to_glist = g_list_prepend(store_to_glist, g_strdup(attr));
+       }
+       return FALSE;
+}
+
+static void custom_attr_window_save_list (void) 
+{
+       GList *cur;
+
+       /* clear existing attribute names list in prefs */
+       cur = prefs_common.addressbook_custom_attributes;
+       while (cur) {
+               g_free(cur->data);
+               cur = cur->next;
+       }
+       g_list_free(prefs_common.addressbook_custom_attributes);
+
+       /* copy attribute names list from store to prefs */
+       store_to_glist = store_to_glist;
+       gtk_tree_model_foreach(gtk_tree_view_get_model
+                       (GTK_TREE_VIEW(custom_attr_window.attr_list)), 
+                       (GtkTreeModelForeachFunc) custom_attr_store_to_glist,
+                   NULL);
+       prefs_common.addressbook_custom_attributes = g_list_reverse(store_to_glist);
+       store_to_glist = NULL;
+}
+
+static GList *custom_attr_default_list(void)
+{
+       /* returned GList must be deallocated by caller */
+       GList *list;
+       gint i;
+       i = 0;
+
+       list = NULL;
+       while (default_addressbook_attributes_table[i]) {
+               list = g_list_prepend(
+                       list, g_strdup(default_addressbook_attributes_table[i]));
+               i++;
+       }
+       list = g_list_reverse(list);
+       return list;
+}
+
+GList *addressbook_update_custom_attr_from_prefs(void)
+{
+       /* load addressbook custom attribute names list from file */
+       /* use a list of default attribute names if storage file doesn't exist */
+       GList *list;
+       GList *default_attr_list;
+       GList *cur;
+
+       /* load table into glist */
+       default_attr_list = custom_attr_default_list();
+
+       list =  prefs_common_read_history_from_dir_with_defaults(ADDRBOOK_DIR,
+                               ADDRESSBOOK_CUSTOM_ATTRIBUTES,
+                               default_attr_list);
+
+       /* free glist if it's the one we return (the default one) */
+       if (list != default_attr_list) {
+               cur = default_attr_list;
+               while (cur) {
+                       g_free(cur->data);
+                       cur = cur->next;
+               }
+               g_list_free(default_attr_list);
+       }
+       return list;
+}
diff --git a/src/addrcustomattr.h b/src/addrcustomattr.h
new file mode 100644 (file)
index 0000000..4427fd7
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 2007 The Claws Mail team
+ *
+ * This program 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * 
+ */
+
+#ifndef __ADDRCUSTOMATTR_H__
+#define __ADDRCUSTOMATTR_H__
+
+void addressbook_custom_attr_edit(void);
+GList *addressbook_update_custom_attr_from_prefs(void);
+
+#endif
index 51dbfd3..ebf8873 100644 (file)
 #include "exphtmldlg.h"
 #include "expldifdlg.h"
 #include "browseldap.h"
+#include "addrcustomattr.h"
 
 typedef enum
 {
@@ -370,6 +371,7 @@ static void addressbook_list_select_remove  ( AddrItemObject    *aio );
 
 static void addressbook_import_ldif_cb         ( void );
 static void addressbook_find_duplicates_cb     ( void );
+static void addressbook_edit_custom_attr_cb    ( void );
 static void addressbook_import_mutt_cb         ( void );
 static void addressbook_import_pine_cb         ( void );
 static void addressbook_export_html_cb         ( void );
@@ -467,6 +469,7 @@ static GtkItemFactoryEntry addressbook_entries[] =
        {N_("/_Tools/Export LDI_F..."), NULL,           addressbook_export_ldif_cb,     0, NULL, NULL},
        {N_("/_Tools/---"),             NULL,           NULL, 0, "<Separator>", NULL},
        {N_("/_Tools/Find duplicates..."), NULL, addressbook_find_duplicates_cb,        0, NULL, NULL},
+       {N_("/_Tools/Edit custom attributes..."), NULL, addressbook_edit_custom_attr_cb,        0, NULL, NULL},
        {N_("/_Help"),                  NULL,           NULL, 0, "<Branch>", NULL},
        {N_("/_Help/_About"),           NULL,           about_show, 0, NULL, NULL}
 };
@@ -5404,6 +5407,11 @@ static void addressbook_find_duplicates_cb(void)
        addrduplicates_find(GTK_WINDOW(addrbook.window));       
 }
 
+static void addressbook_edit_custom_attr_cb(void)
+{
+       addressbook_custom_attr_edit();
+}
+               
 static void addressbook_start_drag(GtkWidget *widget, gint button, 
                                   GdkEvent *event,
                                   void *data)
@@ -5590,4 +5598,3 @@ static void addressbook_drag_received_cb(GtkWidget        *widget,
 /*
 * End of Source.
 */
-
index 05da6a0..814b91f 100644 (file)
@@ -72,6 +72,7 @@
 #define SUMMARY_SEARCH_BODY_HISTORY    "summary_searchbody_history"
 #define SUMMARY_SEARCH_ADV_CONDITION_HISTORY   "summarysearch_adv_history"
 #define MESSAGE_SEARCH_HISTORY "messagesearch_history"
+#define ADDRESSBOOK_CUSTOM_ATTRIBUTES "attributesrc"
 #define TEMPLATE_DIR           "templates"
 #define TMP_DIR                        "tmp"
 #define UIDL_DIR               "uidl"
index 7b6e40a..139e697 100644 (file)
@@ -47,6 +47,7 @@
 #include "editaddress_other_attributes_ldap.h"
 #include "prefs_common.h"
 #include "menu.h"
+#include "combobox.h"
 
 /* transient data */
 static struct _PersonEdit_dlg personeditdlg;
@@ -272,7 +273,7 @@ static void edit_person_email_clear( gpointer data ) {
 
 static void edit_person_attrib_clear( gpointer data ) {
        if (!personeditdlg.ldap) {
-               gtk_entry_set_text( GTK_ENTRY(personeditdlg.entry_atname), "" );
+               gtk_entry_set_text( GTK_ENTRY(GTK_BIN(personeditdlg.entry_atname)->child), "" );
                gtk_entry_set_text( GTK_ENTRY(personeditdlg.entry_atvalue), "" );
        }
 }
@@ -523,7 +524,7 @@ static void edit_person_load_attrib( ItemPerson *person ) {
 static void edit_person_attrib_list_selected( GtkCList *clist, gint row, gint column, GdkEvent *event, gpointer data ) {
        UserAttribute *attrib = gtk_clist_get_row_data( clist, row );
        if( attrib && !personeditdlg.read_only && !personeditdlg.ldap ) {
-               gtk_entry_set_text( GTK_ENTRY(personeditdlg.entry_atname), attrib->name );
+               gtk_entry_set_text( GTK_ENTRY(GTK_BIN(personeditdlg.entry_atname)->child ), attrib->name );
                gtk_entry_set_text( GTK_ENTRY(personeditdlg.entry_atvalue), attrib->value );
                gtk_widget_set_sensitive(personeditdlg.attrib_del, TRUE);
        } else {
@@ -562,7 +563,7 @@ static UserAttribute *edit_person_attrib_edit( gboolean *error, UserAttribute *a
        gchar *sName, *sValue, *sName_, *sValue_;
 
        *error = TRUE;
-       sName_ = gtk_editable_get_chars( GTK_EDITABLE(personeditdlg.entry_atname), 0, -1 );
+       sName_ = gtk_editable_get_chars( GTK_EDITABLE(GTK_BIN(personeditdlg.entry_atname)->child), 0, -1 );
        sValue_ = gtk_editable_get_chars( GTK_EDITABLE(personeditdlg.entry_atvalue), 0, -1 );
        sName = mgu_email_check_empty( sName_ );
        sValue = mgu_email_check_empty( sValue_ );
@@ -1202,17 +1203,19 @@ static gboolean attrib_adding = FALSE, attrib_saving = FALSE;
 static void edit_person_entry_att_changed (GtkWidget *entry, gpointer data)
 {
        gboolean non_empty = gtk_clist_get_row_data(GTK_CLIST(personeditdlg.clist_attrib), 0) != NULL;
+       const gchar *atname;
 
        if (personeditdlg.read_only || personeditdlg.ldap)
                return;
 
-       if (gtk_entry_get_text(GTK_ENTRY(personeditdlg.entry_atname)) == NULL
-       ||  strlen(gtk_entry_get_text(GTK_ENTRY(personeditdlg.entry_atname))) == 0) {
+       atname = gtk_entry_get_text(GTK_ENTRY(GTK_BIN(personeditdlg.entry_atname)->child));
+       if ( atname == NULL
+       ||  strlen(atname) == 0) {
                gtk_widget_set_sensitive(personeditdlg.attrib_add,FALSE);
                gtk_widget_set_sensitive(personeditdlg.attrib_mod,FALSE);
                attrib_adding = FALSE;
                attrib_saving = FALSE;
-       } else if (list_find_attribute(gtk_entry_get_text(GTK_ENTRY(personeditdlg.entry_atname)))) {
+       } else if (list_find_attribute(atname)) {
                gtk_widget_set_sensitive(personeditdlg.attrib_add,FALSE);
                gtk_widget_set_sensitive(personeditdlg.attrib_mod,non_empty);
                attrib_adding = FALSE;
@@ -1318,7 +1321,7 @@ static void addressbook_edit_person_page_attrib( gint pageNum, gchar *pageLbl )
        gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), GTK_FILL, 0, 0, 0);
        gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
 
-       entry_name = gtk_entry_new();
+       entry_name = gtk_combo_box_entry_new_text ();
        gtk_table_attach(GTK_TABLE(table), entry_name, 1, 2, top, (top + 1), GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
 
        /* Next row */
@@ -1334,7 +1337,7 @@ static void addressbook_edit_person_page_attrib( gint pageNum, gchar *pageLbl )
        gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, GTK_FILL, 0, 0, 0);
        gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
 
-       entry_name = gtk_entry_new();
+       entry_name = gtk_combo_box_entry_new_text ();
        gtk_table_attach(GTK_TABLE(table), entry_name, 1, 2, 0, 1, GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
 
        /* Next row */
@@ -1346,6 +1349,11 @@ static void addressbook_edit_person_page_attrib( gint pageNum, gchar *pageLbl )
        entry_value = gtk_entry_new();
        gtk_table_attach(GTK_TABLE(table), entry_value, 3, 4, 0, 1, GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
 #endif
+       gtk_combo_box_set_active(GTK_COMBO_BOX(entry_name), -1);
+       if (prefs_common.addressbook_custom_attributes)
+               combobox_set_popdown_strings(GTK_COMBO_BOX(entry_name),
+                               prefs_common.addressbook_custom_attributes);
+
        /* Button box */
        vboxb = gtk_vbox_new( FALSE, 4 );
        gtk_box_pack_start(GTK_BOX(hbox), vboxb, FALSE, FALSE, 2);
@@ -1737,6 +1745,16 @@ no_img:
        return person;
 }
 
+void addressbook_edit_reload_attr_list( void )
+{
+       if (personeditdlg.entry_atname) {
+               combobox_unset_popdown_strings(GTK_COMBO_BOX(personeditdlg.entry_atname));
+               if (prefs_common.addressbook_custom_attributes)
+                       combobox_set_popdown_strings(GTK_COMBO_BOX(personeditdlg.entry_atname),
+                                       prefs_common.addressbook_custom_attributes);
+       }
+}
+
 /*
 * End of Source.
 */
index 27e2c15..8f2221a 100644 (file)
@@ -33,5 +33,6 @@ ItemPerson *addressbook_edit_person( AddressBookFile *abf, ItemFolder *parent, I
 void addressbook_edit_person_invalidate( AddressBookFile *abf, ItemFolder *parent_folder,
                                                                                 ItemPerson *person );
 void addressbook_edit_person_widgetset_hide( void );
+void addressbook_edit_reload_attr_list( void );
 
 #endif /* __EDITADDRESS_H__ */
index a936c76..2892129 100644 (file)
@@ -1084,21 +1084,30 @@ static PrefParam param[] = {
 };
 
 /*
- * Read history list from the specified history file
+ * Read history list from the specified history file in the specified directory (subdir of rc_dir)
+ * Fallback to default_list if history file is not found
  */
-static GList *prefs_common_read_history(const gchar *history) 
+GList *prefs_common_read_history_from_dir_with_defaults(const gchar *dirname, const gchar *history,
+                                                                                                                         GList *default_list)
 {
        FILE *fp;
        gchar *path;
        gchar buf[PREFSBUFSIZE];
        GList *tmp = NULL;
 
+       if (dirname) {
+               path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, dirname,
+                                  G_DIR_SEPARATOR_S, history,
+                                  NULL);
+       } else {
        path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, history,
                           NULL);
+       }
        if ((fp = g_fopen(path, "rb")) == NULL) {
                if (ENOENT != errno) FILE_OP_ERROR(path, "fopen");
                g_free(path);
-               return NULL;
+               /* returns default list if set, otherwise NULL */
+               return default_list;
        }
        g_free(path);
        while (fgets(buf, sizeof(buf), fp) != NULL) {
@@ -1113,6 +1122,22 @@ static GList *prefs_common_read_history(const gchar *history)
        return tmp;
 }
 
+/*
+ * Read history list from the specified history file in the specified directory (subdir of rc_dir)
+ */
+static GList *prefs_common_read_history_from_dir(const gchar *dirname, const gchar *history) 
+{
+       return prefs_common_read_history_from_dir_with_defaults(dirname, history, NULL);
+}
+
+/*
+ * Read history list from the specified history file
+ */
+static GList *prefs_common_read_history(const gchar *history) 
+{
+       return prefs_common_read_history_from_dir(NULL, history);
+}
+
 void prefs_common_read_config(void)
 {
        gchar *rcpath;
@@ -1159,6 +1184,8 @@ void prefs_common_read_config(void)
        prefs_common.message_search_history =
                prefs_common_read_history(MESSAGE_SEARCH_HISTORY);
 
+       prefs_common.addressbook_custom_attributes = addressbook_update_custom_attr_from_prefs();
+
        colorlabel_update_colortable_from_prefs();
 }
 
@@ -1170,16 +1197,22 @@ if (!(func)) \
 } \
 
 /*
- * Save history list to the specified history file
+ * Save history list to the specified history file in the specified directory (subdir of rc_dir)
  */
-static void prefs_common_save_history(const gchar *history, GList *list)
+static void prefs_common_save_history_to_dir(const gchar *dirname, const gchar *history, GList *list)
 {
        GList *cur;
        FILE *fp;
        gchar *path, *tmp_path;
 
+       if (dirname) {
+               path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, dirname,
+                                  G_DIR_SEPARATOR_S, history,
+                                  NULL);
+       } else {
        path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, history,
                           NULL);
+       }
        tmp_path = g_strconcat(path, ".tmp", NULL);
 
        if ((fp = g_fopen(tmp_path, "wb")) == NULL) {
@@ -1213,6 +1246,14 @@ out:
        g_free(path);
 }
 
+/*
+ * Save history list to the specified history file
+ */
+static void prefs_common_save_history(const gchar *history, GList *list)
+{
+       prefs_common_save_history_to_dir(NULL, history, list);
+}
+
 #undef TRY
 
 void prefs_common_write_config(void)
@@ -1238,6 +1279,10 @@ void prefs_common_write_config(void)
                prefs_common.summary_search_adv_condition_history);
        prefs_common_save_history(MESSAGE_SEARCH_HISTORY, 
                prefs_common.message_search_history);
+
+       prefs_common_save_history_to_dir(ADDRBOOK_DIR,
+               ADDRESSBOOK_CUSTOM_ATTRIBUTES, 
+               prefs_common.addressbook_custom_attributes);
 }
 
 /* make a copy of string 'in' into buffer 'out'. un-escape \ sequences.
index d0f474e..ac93d85 100644 (file)
@@ -332,6 +332,7 @@ struct _PrefsCommon
        gboolean addressbook_use_editaddress_dialog;
        gint addressbook_hpaned_pos;
        gint addressbook_vpaned_pos;
+       GList *addressbook_custom_attributes;
 
        /* Interface */
        gboolean layout_mode;
@@ -482,6 +483,8 @@ struct _PrefsCommon
 
 extern PrefsCommon prefs_common;
 
+GList *prefs_common_read_history_from_dir_with_defaults(const gchar *dirname, const gchar *history,
+                                                                                                                         GList *default_list);
 void prefs_common_read_config  (void);
 void prefs_common_write_config (void);
 void prefs_common_open         (void);