#include "addrbook.h"
#include "addrindex.h"
#include "addressadd.h"
+#include "addressbook_foldersel.h"
#include "vcard.h"
#include "editvcard.h"
#include "editgroup.h"
#include "importldif.h"
#include "importmutt.h"
#include "importpine.h"
+#include "manual.h"
#ifdef USE_JPILOT
#include "jpilot.h"
N_LIST_COLS = 3
} AddressListColumns;
+typedef struct {
+ AddressBookFile *book;
+ ItemFolder *folder;
+} FolderInfo;
+
+typedef struct {
+ gchar **folder_path;
+ gboolean matched;
+ gint index;
+ AddressDataSource *book;
+ ItemFolder *folder;
+} FolderPathMatch;
+
+static gchar *list_titles[] = { N_("Name"),
+ N_("Email Address"),
+ N_("Remarks") };
+
#define COL_NAME_WIDTH 164
#define COL_ADDRESS_WIDTH 156
static gint addressbook_treenode_compare_func (GtkCList *clist,
gconstpointer ptr1,
gconstpointer ptr2);
-static gint addressbook_list_compare_func (GtkCList *clist,
- gconstpointer ptr1,
- gconstpointer ptr2);
static void addressbook_folder_load_one_person (GtkCTree *clist,
ItemPerson *person,
AddressTypeControlItem *atci,
prefs_common.addressbookwin_height = allocation->height;
}
+static gint sort_column_number = 0;
+static GtkSortType sort_column_type = GTK_SORT_ASCENDING;
+
+static gint list_case_sort(
+ GtkCList *clist, gconstpointer ptr1, gconstpointer ptr2 )
+{
+ GtkCListRow *row1 = (GtkCListRow *) ptr1;
+ GtkCListRow *row2 = (GtkCListRow *) ptr2;
+ gchar *name1 = NULL, *name2 = NULL;
+ AddrItemObject *aio1 = ((GtkCListRow *)ptr1)->data;
+ AddrItemObject *aio2 = ((GtkCListRow *)ptr2)->data;
+
+ if( aio1->type == aio2->type ) {
+ if( row1 )
+ name1 = GTK_CELL_TEXT (row1->cell[sort_column_number])->text;
+ if( row2 )
+ name2 = GTK_CELL_TEXT (row2->cell[sort_column_number])->text;
+ if( ! name1 ) return ( name2 != NULL );
+ if( ! name2 ) return -1;
+ return strcasecmp( name1, name2 );
+ } else {
+ /* Order groups before person */
+ if( aio1->type == ITEMTYPE_GROUP ) {
+ return (sort_column_type==GTK_SORT_ASCENDING) ? -1:+1;
+ } else if( aio2->type == ITEMTYPE_GROUP ) {
+ return (sort_column_type==GTK_SORT_ASCENDING) ? +1:-1;
+ }
+ return 0;
+ }
+}
+
+static void addressbook_sort_list(GtkCList *clist, const gint col,
+ const GtkSortType sort_type)
+{
+ gint pos;
+ GtkWidget *hbox, *label, *arrow;
+
+ sort_column_number = col;
+ sort_column_type = sort_type;
+ gtk_clist_set_compare_func(clist, list_case_sort);
+ gtk_clist_set_sort_type(clist, sort_type);
+ gtk_clist_set_sort_column(clist, col);
+
+ gtk_clist_freeze(clist);
+ gtk_clist_sort(clist);
+
+ for(pos = 0 ; pos < N_LIST_COLS ; pos++) {
+ hbox = gtk_hbox_new(FALSE, 4);
+ label = gtk_label_new(gettext(list_titles[pos]));
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+
+ if(pos == col) {
+ arrow = gtk_arrow_new(sort_type == GTK_SORT_ASCENDING ?
+ GTK_ARROW_DOWN : GTK_ARROW_UP, GTK_SHADOW_IN);
+ gtk_box_pack_end(GTK_BOX(hbox), arrow, FALSE, FALSE, 0);
+ }
+
+ gtk_widget_show_all(hbox);
+ gtk_clist_set_column_widget(clist, pos, hbox);
+ }
+
+ gtk_clist_thaw(clist);
+}
+
+static void addressbook_name_clicked(GtkWidget *button, GtkCList *clist)
+{
+ static GtkSortType sort_type = GTK_SORT_ASCENDING;
+
+ sort_type = (sort_type == GTK_SORT_ASCENDING) ? GTK_SORT_DESCENDING :
+ GTK_SORT_ASCENDING;
+ addressbook_sort_list(clist, COL_NAME, sort_type);
+}
+
+static void addressbook_address_clicked(GtkWidget *button, GtkCList *clist)
+{
+ static GtkSortType sort_type = GTK_SORT_ASCENDING;
+
+ sort_type = (sort_type == GTK_SORT_ASCENDING) ? GTK_SORT_DESCENDING :
+ GTK_SORT_ASCENDING;
+ addressbook_sort_list(clist, COL_ADDRESS, sort_type);
+}
+
+static void addressbook_remarks_clicked(GtkWidget *button, GtkCList *clist)
+{
+ static GtkSortType sort_type = GTK_SORT_ASCENDING;
+
+ sort_type = (sort_type == GTK_SORT_ASCENDING) ? GTK_SORT_DESCENDING :
+ GTK_SORT_ASCENDING;
+ addressbook_sort_list(clist, COL_REMARKS, sort_type);
+}
+
/*
* Create the address book widgets. The address book contains two CTree widgets: the
* address index tree on the left and the address list on the right.
GtkWidget *statusbar;
GtkWidget *hbbox;
GtkWidget *hsbox;
+ GtkWidget *help_btn;
GtkWidget *del_btn;
GtkWidget *edit_btn;
GtkWidget *reg_btn;
GList *nodeIf;
gchar *index_titles[N_INDEX_COLS];
- gchar *list_titles[N_LIST_COLS];
gchar *text;
gint i;
debug_print("Creating addressbook window...\n");
index_titles[COL_SOURCES] = _("Sources");
- list_titles[COL_NAME] = _("Name");
- list_titles[COL_ADDRESS] = _("Email Address");
- list_titles[COL_REMARKS] = _("Remarks");
/* Address book window */
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
COL_NAME_WIDTH);
gtk_clist_set_column_width(GTK_CLIST(clist), COL_ADDRESS,
COL_ADDRESS_WIDTH);
- gtk_clist_set_compare_func(GTK_CLIST(clist),
- addressbook_list_compare_func);
+ addressbook_sort_list(GTK_CLIST(clist), COL_NAME, GTK_SORT_ASCENDING);
+ g_signal_connect(G_OBJECT(GTK_CLIST(clist)->column[COL_NAME].button),
+ "clicked", G_CALLBACK(addressbook_name_clicked), clist);
+ g_signal_connect(G_OBJECT(GTK_CLIST(clist)->column[COL_ADDRESS].button),
+ "clicked", G_CALLBACK(addressbook_address_clicked), clist);
+ g_signal_connect(G_OBJECT(GTK_CLIST(clist)->column[COL_REMARKS].button),
+ "clicked", G_CALLBACK(addressbook_remarks_clicked), clist);
+
for (i = 0; i < N_LIST_COLS; i++)
GTK_WIDGET_UNSET_FLAGS(GTK_CLIST(clist)->column[i].button,
GTK_CAN_FOCUS);
gtk_container_set_border_width(GTK_CONTAINER(hbbox), 4);
gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
+ gtkut_stock_button_add_help(hbbox, &help_btn);
+
#if GTK_CHECK_VERSION(2, 6, 0)
edit_btn = gtk_button_new_from_stock(GTK_STOCK_EDIT);
#else
GTK_WIDGET_SET_FLAGS(lup_btn, GTK_CAN_DEFAULT);
gtk_box_pack_start(GTK_BOX(hbox), lup_btn, TRUE, TRUE, 0);
+ g_signal_connect(G_OBJECT(help_btn), "clicked",
+ G_CALLBACK(manual_open_with_anchor_cb),
+ MANUAL_ANCHOR_ADDRBOOK);
+
g_signal_connect(G_OBJECT(edit_btn), "clicked",
G_CALLBACK(addressbook_edit_clicked), NULL);
g_signal_connect(G_OBJECT(del_btn), "clicked",
addrbook.status_cid = gtk_statusbar_get_context_id(
GTK_STATUSBAR(statusbar), "Addressbook Window" );
+ addrbook.help_btn = help_btn;
addrbook.edit_btn = edit_btn;
addrbook.del_btn = del_btn;
addrbook.reg_btn = reg_btn;
addrindex_ds_set_access_flag( ds, &tVal );
gtk_ctree_expand( ctree, node );
}
+ } else {
+ addressbook_set_clist(NULL, TRUE);
}
/* Update address list */
if( person == NULL ) return;
- text[COL_NAME] = NULL;
+ text[COL_NAME] = "";
node = person->listEMail;
while( node ) {
ItemEMail *email = node->data;
if( ! haveAddr ) {
/* Have name without EMail */
text[COL_NAME] = ADDRITEM_NAME(person);
- text[COL_ADDRESS] = NULL;
- text[COL_REMARKS] = NULL;
+ text[COL_ADDRESS] = "";
+ text[COL_REMARKS] = "";
nodePerson = gtk_sctree_insert_node(
clist, NULL, NULL,
text, FOLDER_SPACING,
ItemGroup *group = items->data;
if( group == NULL ) continue;
text[COL_NAME] = ADDRITEM_NAME(group);
- text[COL_ADDRESS] = NULL;
- text[COL_REMARKS] = NULL;
+ text[COL_ADDRESS] = "";
+ text[COL_REMARKS] = "";
nodeGroup = gtk_sctree_insert_node(clist, NULL, NULL,
text, FOLDER_SPACING,
atci->iconXpm, atci->maskXpm,
}
else {
msg = _("Old address book converted,\n"
- "could not save new address index file" );
+ "could not save new address index file." );
}
}
else {
}
else {
msg = _("Could not convert address book,\n"
- "could not create new address book files." );
+ "could not save new address index file." );
}
}
else {
return g_utf8_collate( name1, name2 );
}
-/*
-* Comparison using object names and types.
-*/
-static gint addressbook_list_compare_func(
- GtkCList *clist, gconstpointer ptr1, gconstpointer ptr2 )
-{
- AddrItemObject *aio1 = ((GtkCListRow *)ptr1)->data;
- AddrItemObject *aio2 = ((GtkCListRow *)ptr2)->data;
- gchar *name1 = NULL, *name2 = NULL;
-
- /* Order by cell contents */
- name1 = ADDRITEM_NAME( aio1 );
- name2 = ADDRITEM_NAME( aio2 );
-
- if( aio1->type == aio2->type ) {
- /* Order by name */
- if( ! name1 ) return ( name2 != NULL );
- if( ! name2 ) return -1;
- return g_utf8_collate( name1, name2 );
- }
- else {
- /* Order groups before person */
- if( aio1->type == ITEMTYPE_GROUP ) {
- return -1;
- }
- else if( aio2->type == ITEMTYPE_GROUP ) {
- return 1;
- }
- return 0;
- }
-}
-
static void addressbook_new_book_cb( gpointer data, guint action, GtkWidget *widget ) {
AdapterDSource *ads;
AdapterInterface *adapter;
atci->showInTree = TRUE;
atci->treeExpand = TRUE;
atci->treeLeaf = FALSE;
- atci->displayName = _( "LDAP Server" );
+ atci->displayName = _( "LDAP servers" );
atci->iconXpm = ldapxpm;
atci->maskXpm = ldapxpmmask;
atci->iconXpmOpen = ldapxpm;
return TRUE;
}
+/* ***********************************************************************
+ * Book/folder selection.
+ * ***********************************************************************
+ */
+
+/*
+ * This function is used by the matcher dialog to select a book/folder.
+ */
+gboolean addressbook_folder_selection( gchar **folderpath )
+{
+ AddressBookFile *book = NULL;
+ ItemFolder *folder = NULL;
+ gchar *path;
+
+ g_return_val_if_fail( folderpath != NULL, FALSE);
+
+ path = *folderpath;
+ *folderpath = NULL;
+ if ( addressbook_foldersel_selection( _addressIndex_, &book, &folder, path )
+ && book != NULL ) {
+ if ( folder != NULL) {
+ gchar *tmp = NULL;
+ gchar *oldtmp = NULL;
+ AddrItemObject *obj = NULL;
+
+ /* walk thru folder->parent to build the full folder path */
+ /* TODO: wwp: optimize this */
+ obj = &folder->obj;
+ tmp = g_strdup(obj->uid);
+ while ( obj->parent ) {
+ obj = obj->parent;
+ if ( obj->name != NULL ) {
+ oldtmp = g_strdup(tmp);
+ g_free(tmp);
+ tmp = g_strdup_printf("%s/%s", obj->uid, oldtmp);
+ g_free(oldtmp);
+ }
+ }
+ *folderpath = g_strdup_printf("%s/%s", book->fileName, tmp);
+ g_free(tmp);
+ } else {
+ *folderpath = g_strdup_printf("%s", book->fileName);
+ }
+ debug_print( "addressbook_foldersel: %s\n", *folderpath);
+ return (*folderpath != NULL);
+ }
+ return FALSE;
+}
+
+/* ***********************************************************************
+ * Book/folder checking.
+ * ***********************************************************************
+ */
+
+static FolderInfo *addressbook_peek_subfolder_exists_create_folderinfo( AddressBookFile *abf, ItemFolder *folder )
+{
+ FolderInfo *fi = g_new0( FolderInfo, 1 );
+ fi->book = abf;
+ fi->folder = folder;
+ return fi;
+}
+
+static void addressbook_peek_subfolder_exists_load_folder( ItemFolder *parentFolder,
+ FolderInfo *fiParent, FolderPathMatch *match )
+{
+ GList *list;
+ ItemFolder *folder;
+ gchar *fName;
+ FolderInfo *fi;
+ FolderPathMatch *nextmatch = NULL;
+
+ list = parentFolder->listFolder;
+ while ( list ) {
+ folder = list->data;
+ fName = g_strdup( ADDRITEM_NAME(folder) );
+
+ /* match folder name, match pointer will be set to NULL if next recursive call
+ doesn't need to match subfolder name */
+ if ( match != NULL &&
+ match->matched == FALSE ) {
+ if ( strcmp(match->folder_path[match->index], folder->obj.uid) == 0 ) {
+ /* folder name matches, prepare next subfolder match */
+ debug_print("matched folder name '%s'\n", fName);
+ match->index++;
+ if ( match->folder_path[match->index] == NULL ) {
+ /* we've matched all elements */
+ match->matched = TRUE;
+ match->folder = folder;
+ debug_print("book/folder path matched!\n");
+ } else {
+ /* keep on matching */
+ nextmatch = match;
+ }
+ }
+ }
+
+ g_free( fName );
+
+ fi = addressbook_peek_subfolder_exists_create_folderinfo( fiParent->book, folder );
+ addressbook_peek_subfolder_exists_load_folder( folder, fi, nextmatch );
+ list = g_list_next( list );
+ }
+}
+
+/*
+ * This function is used by to check if a matcher book/folder path corresponds to an
+ existing addressbook book/folder ("" or "Any" are considered as valid, NULL invalid).
+ */
+
+gboolean addressbook_peek_folder_exists( gchar *folderpath,
+ AddressDataSource **book,
+ ItemFolder **folder )
+{
+ AddressDataSource *ds;
+ GList *list, *nodeDS;
+ ItemFolder *rootFolder;
+ AddressBookFile *abf;
+ FolderInfo *fi;
+ FolderPathMatch folder_path_match = { NULL, FALSE, 0, NULL, NULL };
+ FolderPathMatch *nextmatch;
+
+ if ( book )
+ *book = NULL;
+ if ( folder )
+ *folder = NULL;
+
+ if ( folderpath == NULL )
+ return FALSE;
+
+ if ( strcasecmp(folderpath, _("Any")) == 0 || *folderpath == '\0' )
+ return TRUE;
+
+ /* split the folder path we've received, we'll try to match this path, subpath by
+ subpath against the book/folder structure in order */
+ folder_path_match.folder_path = g_strsplit( folderpath, "/", 256 );
+
+ list = addrindex_get_interface_list( _addressIndex_ );
+ while ( list ) {
+ AddressInterface *interface = list->data;
+ if ( interface->type == ADDR_IF_BOOK ) {
+ nodeDS = interface->listSource;
+ while ( nodeDS ) {
+ ds = nodeDS->data;
+
+ /* Read address book */
+ if( ! addrindex_ds_get_read_flag( ds ) ) {
+ addrindex_ds_read_data( ds );
+ }
+
+ /* Add node for address book */
+ abf = ds->rawDataSource;
+
+ /* try to match subfolders if this book is the right book
+ (and if there's smth to match, and not yet matched) */
+ nextmatch = NULL;
+ if ( folder_path_match.folder_path != NULL &&
+ folder_path_match.matched == FALSE &&
+ strcmp(folder_path_match.folder_path[0], abf->fileName) == 0 ) {
+ debug_print("matched book name '%s'\n", abf->fileName);
+ folder_path_match.index = 1;
+ if ( folder_path_match.folder_path[folder_path_match.index] == NULL ) {
+ /* we've matched all elements */
+ folder_path_match.matched = TRUE;
+ folder_path_match.book = ds;
+ debug_print("book path matched!\n");
+ } else {
+ /* keep on matching */
+ nextmatch = &folder_path_match;
+ }
+ }
+
+ fi = addressbook_peek_subfolder_exists_create_folderinfo( abf, NULL );
+
+ rootFolder = addrindex_ds_get_root_folder( ds );
+ addressbook_peek_subfolder_exists_load_folder( rootFolder, fi, nextmatch );
+
+ nodeDS = g_list_next( nodeDS );
+ }
+ }
+ list = g_list_next( list );
+ }
+
+ g_strfreev( folder_path_match.folder_path );
+
+ if ( book )
+ *book = folder_path_match.book;
+ if ( folder )
+ *folder = folder_path_match.folder;
+ return folder_path_match.matched;
+}
+
+
/* **********************************************************************
* Address Import.
* ***********************************************************************