From 3370ba68ac9cbbd583da6997c039b29b0fc43b7a Mon Sep 17 00:00:00 2001 From: Cedric Duval Date: Mon, 4 Sep 2006 18:29:24 +0000 Subject: [PATCH] New index_format option. --- RELEASE_NOTES | 32 +++++++ abook.c | 2 + abookrc.5 | 87 ++++++----------- configure | 2 +- database.c | 4 +- database.h | 2 +- edit.c | 11 --- filter.c | 82 ++++++++++------ list.c | 250 +++++++++++++++++++++++++++++++++++-------------- list.h | 37 +++++--- misc.c | 13 +++ misc.h | 2 + options.c | 2 +- options.h | 1 + sample.abookrc | 29 ++---- 15 files changed, 353 insertions(+), 203 deletions(-) create mode 100644 RELEASE_NOTES diff --git a/RELEASE_NOTES b/RELEASE_NOTES new file mode 100644 index 0000000..92fcca6 --- /dev/null +++ b/RELEASE_NOTES @@ -0,0 +1,32 @@ +This file lists major changes affecting abook's behavior. +Please read this file carefully when upgrading abook. +A more comprehensive list of changes can be found in the ChangeLog file. + +-- + +0.6.0pre2: + * The four following configuration options have been deprecated and will + no longer be accepted by abook: + * emailpos + * extra_column + * extra_alternative + * extrapos + + They have been replaced with a single more flexible option: + index_format. + + This option is a string defining the format of a line in the main list. + It allows displaying of as many fields as desired, with optional + width limit, as well as an arbitrary number of alternative fields. + +0.6.0pre1 (2006-08-30): + * The 'customfield' command has been obsoleted by a more flexible set + of commands: 'field' and 'view'. + + Those two commands make it possible to define fields and organize + them within tabs as you see fit. + + Not using these commands, the look and feel of previous releases + will mostly be kept the same. Also see the related 'preserve_fields' + configuration variable. + diff --git a/abook.c b/abook.c index 43f0294..c4d239e 100644 --- a/abook.c +++ b/abook.c @@ -132,6 +132,8 @@ init_abook() signal(SIGTERM, quit_abook_sig); + init_index(); + if(init_ui()) exit(EXIT_FAILURE); diff --git a/abookrc.5 b/abookrc.5 index 7c66ef7..638b36f 100644 --- a/abookrc.5 +++ b/abookrc.5 @@ -78,51 +78,41 @@ only preserve the standard fields (see a list in the .TP .B none discards any unknown field. +.RE .IP Default is \fIstandard\fP. -.RE .TP -\fBshow_all_emails\fP=[true|false] -Defines whether all email addresses for a contact are shown in the main list view. Default is true - +\fBindex_format\fP=format_string +Defines the way entries are displayed in the main list. This is a string containing field names enclosed between braces, with an optional width limit specified by a number (right alignment if negative) after the field name and a colon, and an arbitrary number of alternative fields (first with non empty content is to be displayed) separated by vertical bars. For instance: +.RS .TP -\fBemailpos\fP=column -Defines the screen column on the main list where the email address is to begin. Default is 25. - +\fI{name:22}\fP +displays the \fIname\fP field with a maximal width of 22 characters. .TP -\fBextra_column\fP=field -Defines the field to display in the extra (third) column on the main list. Default is "phone" (Home Phone). +\fI{phone:-13|workphone|mobile}\fP +displays (right aligned within a width of 13 characters), either the \fIphone\fP, \fIworkphone\fP or \fImobile\fP field, whichever being the first to be non-empty. +.RE .IP -\fIfield\fP can be any of the following: -.br --1 disabled -.br -phone Home Phone -.br -workphone Work Phone -.br -fax Fax -.br -mobile Mobile Phone -.br -nick Nickname/Alias -.br -url URL -.br -notes Notes +Default is \fI" {name:22} {email:40} {phone:12|workphone|mobile}"\fP .TP -\fBextra_alternative\fP=field -This is an optional setting that allows you to specify an alternative field to be displayed in the extra (third) column if there is no data for the field specified in extra_column for a particular item. The strings for the fields are the same as above. Please note that the data shown where the alternative field has been used will NOT be marked differently in any way from the rest of the extra column. There is no default. +\fBshow_all_emails\fP=[true|false] +Defines whether all email addresses for a contact are shown in the main list view. Default is true. .TP -\fBextrapos\fP=column -Defines the screen column on the main list where the extra field is to begin. Default is 65. - +.PD 0 +\fBemailpos\fP +.TP +.PD 0 +\fBextra_column\fP +.TP +.PD 0 +\fBextra_alternative\fP .TP -\fBmutt_command\fP=command -Defines the command to start mutt. Default is "mutt". +.PD +\fBextrapos\fP +Obsoleted by \fBindex_format\fP. .TP \fBmutt_return_all_emails\fP=[true|false] @@ -180,29 +170,12 @@ set preserve_fields=all # Automatically save database on exit set autosave=true +# Format of entries lines in list +set index_format=" {name:22} {email:40} {phone:12|workphone|mobile}" + # Show all email addresses in list set show_all_emails=true -# Screen column for email field to start -set emailpos=25 - -# Field to be used in the extra column -set extra_column=phone -# frequently used values: -# -1 disabled -# phone Home Phone -# workphone Work Phone -# fax Fax -# mobile Mobile Phone -# nick Nickname/Alias -# url URL - -# -set extra_alternative=-1 - -# Screen column for the extra field to start -set extrapos=65 - # Command used to start mutt set mutt_command=mutt @@ -215,19 +188,19 @@ set print_command=lpr # Command used to start the web browser set www_command=lynx -# address style [eu|us|uk] +# Address style [eu|us|uk] set address_style=eu -# use ASCII characters only +# Use ASCII characters only set use_ascii_only=false # Prevent double entry set add_email_prevent_duplicates=false -# field to be used with "sort by field" command +# Field to be used with "sort by field" command set sort_field=nick -# show cursor in main display +# Show cursor in main display set show_cursor=false .fi diff --git a/configure b/configure index b904366..02463ff 100755 --- a/configure +++ b/configure @@ -2072,7 +2072,7 @@ fi # Define the identity of the package. PACKAGE=abook - VERSION=0.5.5 + VERSION=0.6.0pre1 cat >>confdefs.h <<_ACEOF diff --git a/database.c b/database.c index 632ad56..da6890f 100644 --- a/database.c +++ b/database.c @@ -121,7 +121,7 @@ real_find_field(char *key, abook_field_list *list, int *number) } void -get_field_keyname(int i, char **key, char **name) +get_field_info(int i, char **key, char **name, int *type) { abook_field_list *cur = fields_list; int j; @@ -135,6 +135,8 @@ get_field_keyname(int i, char **key, char **name) *key = (i < 0) ? NULL : cur->field->key; if(name) *name = (i < 0) ? NULL : cur->field->name; + if(type) + *type = (i < 0) ? -1 : cur->field->type; } void diff --git a/database.h b/database.h index 0304841..a81bd6e 100644 --- a/database.h +++ b/database.h @@ -66,7 +66,7 @@ abook_field *real_find_field(char *key, abook_field_list *list, int *nb); #define find_field(key, list) real_find_field(key, list, NULL) #define find_field_number(key, pt_nb) real_find_field(key, NULL, pt_nb) #define find_declared_field(key) find_field(key,NULL) -void get_field_keyname(int i, char **key, char **name); +void get_field_info(int i, char **key, char **name, int *type); void add_field(abook_field_list **list, abook_field *f); char *declare_new_field(char *key, char *name, char *type, int accept_standard); void init_standard_fields(); diff --git a/edit.c b/edit.c index 5f889f4..5605a0f 100644 --- a/edit.c +++ b/edit.c @@ -459,17 +459,6 @@ parse_date_string(char *s, int *day, int *month, int *year) return is_valid_date(*day, *month, *year); } -static int -is_number(char *s) -{ - char *p; - - for(p = s; *p; p++) - if(!isdigit(*p)) - return FALSE; - return TRUE; -} - static void edit_date(int item, int nb) { diff --git a/filter.c b/filter.c index 671d2a4..70ec2d4 100644 --- a/filter.c +++ b/filter.c @@ -832,39 +832,56 @@ ldif_export_database(FILE *out, struct db_enumerator e) * html export filter */ -static void html_export_write_head(FILE *out, int extra_column); +static void html_export_write_head(FILE *out); static void html_export_write_tail(FILE *out); +extern struct index_elem *index_elements; + +static void +html_print_emails(FILE *out, struct list_field *f) +{ + abook_list *l = csv_to_abook_list(f->data); + + for(; l; l = l->next) { + fprintf(out, "%s", l->data, l->data); + if(l->next) + fprintf(out, ", "); + } + + abook_list_free(&l); +} + static int html_export_database(FILE *out, struct db_enumerator e) { - char tmp[MAX_EMAILSTR_LEN], *emails; - int extra_column; + struct list_field f; + struct index_elem *cur; if(list_is_empty()) return 2; - extra_column = init_extra_field(STR_EXTRA_COLUMN); - html_export_write_head(out, extra_column); + init_index(); - db_enumerate_items(e) { - get_first_email(tmp, e.item); - if (*tmp) - fprintf(out, "\n" - "%s" - "\n", - tmp, - db_name_get(e.item)); - else - fprintf(out, "\n%s\n", db_name_get(e.item)); + html_export_write_head(out); - emails = db_email_get(e.item); - fprintf(out, "%s\n", emails); - free(emails); - if(extra_column >= 0) - fprintf(out, "%s\n", - safe_str(db_fget_byid(e.item, extra_column))); - fprintf(out, "\n\n"); + db_enumerate_items(e) { + fprintf(out, ""); + for(cur = index_elements; cur; cur = cur->next) { + if(cur->type != INDEX_FIELD) + continue; + + get_list_field(e.item, cur, &f); + + if(f.type == FIELD_EMAILS) { + fprintf(out, ""); + html_print_emails(out, &f); + fprintf(out, ""); + continue; + } else { + fprintf(out, "%s", safe_str(f.data)); + } + } + fprintf(out, "\n"); } html_export_write_tail(out); @@ -873,9 +890,10 @@ html_export_database(FILE *out, struct db_enumerator e) } static void -html_export_write_head(FILE *out, int extra_column) +html_export_write_head(FILE *out) { - char *realname = get_real_name(), *extra_column_name = NULL; + char *realname = get_real_name(), *str; + struct index_elem *cur; fprintf(out, "\n"); fprintf(out, "\n\n %s's addressbook", @@ -884,11 +902,13 @@ html_export_write_head(FILE *out, int extra_column) fprintf(out, "\n

%s's addressbook

\n", realname ); fprintf(out, "

\n\n"); - fprintf(out, "\n"); - fprintf(out, "\n"); - if(extra_column >= 0) { - get_field_keyname(extra_column, NULL, &extra_column_name); - fprintf(out, "", safe_str(extra_column_name)); + fprintf(out, "
NameE-mail address(es)%s
\n"); + for(cur = index_elements; cur; cur = cur->next) { + if(cur->type != INDEX_FIELD) + continue; + + get_field_info(cur->d.field.id, NULL, &str, NULL); + fprintf(out, "", str); } fprintf(out, "\n\n"); @@ -1744,8 +1764,8 @@ text_export_database(FILE * out, struct db_enumerator e) fprintf(out, "\n"); for(j = PHONE; j <= MOBILEPHONE; j++) if(db_fget(e.item, j)) { - get_field_keyname(field_id(j), - NULL, &str); + get_field_info(field_id(j), + NULL, &str, NULL); fprintf(out, "%s: %s\n", str, db_fget(e.item, j)); } diff --git a/list.c b/list.c index 53681b3..3051691 100644 --- a/list.c +++ b/list.c @@ -25,30 +25,103 @@ int curitem = -1; int first_list_item = -1; char *selected = NULL; -int extra_column = -1; -int extra_alternative = -1; - extern abook_field_list *fields_list; +struct index_elem *index_elements = NULL; static WINDOW *list = NULL; -int -init_extra_field(enum str_opts option) +static void +index_elem_add(int type, char *a, char *b) { - int ret = -1; - char *option_str; + struct index_elem *tmp = NULL, *cur, *cur2; + int field, len = 0; - option_str = opt_get_str(option); + if(!a || !*a) + return; + + switch(type) { + case INDEX_TEXT: + tmp = xmalloc(sizeof(struct index_elem)); + tmp->d.text = xstrdup(a); + break; + case INDEX_FIELD: /* fall through */ + case INDEX_ALT_FIELD: + find_field_number(a, &field); + if(field == -1) + return; + len = (b && *b && is_number(b)) ? atoi(b) : 0; + tmp = xmalloc(sizeof(struct index_elem)); + tmp->d.field.id = field; + tmp->d.field.len = len; + break; + default: + assert(0); + } + tmp->type = type; + tmp->next = NULL; + tmp->d.field.next = NULL; + + if(!index_elements) { /* first element */ + index_elements = tmp; + return; + } - if(option_str && *option_str) { - find_field_number(option_str, &ret); + for(cur = index_elements; cur->next; cur = cur->next) + ; + if(type != INDEX_ALT_FIELD) + cur->next = tmp; + else { /* add as an alternate field */ + tmp->d.field.len = cur->d.field.len; + for(cur2 = cur; cur2->d.field.next; cur2 = cur2->d.field.next) + ; + cur2->d.field.next = tmp; + } +} - if(!strcmp(option_str, "name") || !strcmp(option_str, "email")) - ret = -1; +static void +parse_index_format(char *s) +{ + char *p, *start, *lstart = NULL; + int in_field = 0, in_alternate = 0, in_length = 0, type; + + p = start = s; + + while(*p) { + if(*p == '{' && !in_field) { + *p = 0; + index_elem_add(INDEX_TEXT, start, NULL); + start = ++p; + in_field = 1; + } else if(*p == ':' && in_field && !in_alternate) { + *p = 0; + lstart = ++p; + in_length = 1; + } else if(*p == '|' && in_field) { + *p = 0; + type = in_alternate ? INDEX_ALT_FIELD : INDEX_FIELD; + index_elem_add(type, start, in_length ? lstart : NULL); + start = ++p; + in_length = 0; + in_alternate = 1; + } else if(*p == '}' && in_field) { + *p = 0; + type = in_alternate ? INDEX_ALT_FIELD : INDEX_FIELD; + index_elem_add(type, start, in_length ? lstart : NULL); + start = ++p; + in_field = in_alternate = in_length = 0; + } else + p++; } + if(!in_field) + index_elem_add(INDEX_TEXT, start, NULL); +} - return ret; +void +init_index() +{ + assert(!index_elements); + parse_index_format(opt_get_str(STR_INDEX_FORMAT)); } void @@ -56,13 +129,6 @@ init_list() { list = newwin(LIST_LINES, LIST_COLS, LIST_TOP, 0); scrollok(list, TRUE); - - /* - * init extra_column and extra alternative - */ - - extra_column = init_extra_field(STR_EXTRA_COLUMN); - extra_alternative = init_extra_field(STR_EXTRA_ALTERNATIVE); } void @@ -72,6 +138,90 @@ close_list() list = NULL; } +void +get_list_field(int item, struct index_elem *e, struct list_field *res) +{ + char *s; + + res->data = s = NULL; + + do { /* find first non-empty field data in the alternate fields list */ + s = db_fget_byid(item, e->d.field.id); + } while(!(s && *s) && ((e = e->d.field.next) != NULL)); + + if(!e || !s || !*s) + return; + + res->data = s; + get_field_info(e->d.field.id, NULL, NULL, &res->type); +} + +static void +print_list_field(int item, int line, int *x_pos, struct index_elem *e) +{ + char *s, *p; + int width, x_start, mustfree = FALSE, len = abs(e->d.field.len); + struct list_field f; + + get_list_field(item, e, &f); + s = f.data; + + if(!s || !*s) { + *x_pos += len; + return; + } + + if(f.type == FIELD_EMAILS && !opt_get_bool(BOOL_SHOW_ALL_EMAILS)) + if((p = strchr(s, ',')) != NULL) { + s = xstrndup(s, p - s); + mustfree = TRUE; + } + + width = len ? bytes2width(s, len) : strwidth(s); + x_start = *x_pos + ((e->d.field.len < 0) ? len - width : 0); + if(width + x_start >= COLS) + width = COLS - x_start; + + if(width) + mvwaddnstr(list, line, x_start, s, width); + + if(mustfree) + free(s); + + *x_pos += len ? len : width; +} + +static void +print_list_line(int item, int line, int highlight) +{ + struct index_elem *cur; + int x_pos = 1; + + scrollok(list, FALSE); + if(highlight) + highlight_line(list, line); + + if(selected[item]) + mvwaddch(list, line, 0, '*' ); + + for(cur = index_elements; cur; cur = cur->next) + switch(cur->type) { + case INDEX_TEXT: + mvwaddstr(list, line, x_pos, cur->d.text); + x_pos += strwidth(cur->d.text); + break; + case INDEX_FIELD: + print_list_field(item, line, &x_pos, cur); + break; + default: + assert(0); + } + + scrollok(list, TRUE); + if(highlight) + wstandend(list); +} + void refresh_list() { @@ -113,63 +263,27 @@ refresh_list() wrefresh(list); } -void -print_list_line(int i, int line, int highlight) -{ - int extra = extra_column; - char tmp[MAX_EMAILSTR_LEN], *emails; - int real_emaillen = (extra_column > 0 || extra_alternative > 0) ? - EMAILLEN : COLS - EMAILPOS; - - scrollok(list, FALSE); - if(highlight) - highlight_line(list, line); - - if(selected[i]) - mvwaddch(list, line, 0, '*' ); - - mvwaddnstr(list, line, NAMEPOS, db_name_get(i), - bytes2width(db_name_get(i), NAMELEN)); - - if(opt_get_bool(BOOL_SHOW_ALL_EMAILS)) { - emails = db_email_get(i); - mvwaddnstr(list, line, EMAILPOS, emails, - bytes2width(emails, real_emaillen)); - free(emails); - } else { - get_first_email(tmp, i); - mvwaddnstr(list, line, EMAILPOS, tmp, - bytes2width(tmp, real_emaillen)); - } - - if(extra < 0 || !db_fget_byid(i, extra)) - extra = extra_alternative; - if(extra >= 0) - mvwaddnstr(list, line, EXTRAPOS, - safe_str(db_fget_byid(i, extra)), - bytes2width(safe_str(db_fget_byid(i, extra)), - EXTRALEN)); - - scrollok(list, TRUE); - if(highlight) - wstandend(list); -} - void list_headerline() { + struct index_elem *e; + int x_pos = 1, width; char *str = NULL; #if defined(A_BOLD) && defined(A_NORMAL) attrset(A_BOLD); #endif - mvaddstr(2, NAMEPOS, find_field("name", NULL)->name); - mvaddstr(2, EMAILPOS, find_field("email", NULL)->name); - if(extra_column > 0) { - get_field_keyname(extra_column, NULL, &str); - mvaddnstr(2, EXTRAPOS, str, COLS - EXTRAPOS); - } + for(e = index_elements; e; e = e->next) + if(e->type == INDEX_TEXT) + x_pos += strwidth(e->d.text); + else if(e->type == INDEX_FIELD) { + get_field_info(e->d.field.id, NULL, &str, NULL); + width = e->d.field.len ? abs(e->d.field.len) : strwidth(str); + mvaddnstr(2, x_pos, str, width); + x_pos += width; + } else + assert(0); #if defined(A_BOLD) && defined(A_NORMAL) attrset(A_NORMAL); diff --git a/list.h b/list.h index e968a2f..8f6e4a0 100644 --- a/list.h +++ b/list.h @@ -3,11 +3,34 @@ #include "ui.h" +#define INDEX_TEXT 1 +#define INDEX_FIELD 2 +#define INDEX_ALT_FIELD 3 + +struct index_elem { + int type; + union { + char *text; + struct { + int id; + int len; + struct index_elem *next; + } field; + } d; + struct index_elem *next; +}; + +struct list_field { + char *data; + int type; +}; + +void init_index(); void init_list(); int init_extra_field(enum str_opts option); void close_list(); void refresh_list(); -void print_list_line(int i, int line, int highlight); +void get_list_field(int item, struct index_elem *e, struct list_field *res); void list_headerline(); void scroll_up(); void scroll_down(); @@ -35,19 +58,11 @@ enum { }; #define LIST_TOP 3 -#define LIST_BOTTOM (LINES-2) +#define LIST_BOTTOM (LINES - 2) -#define LIST_LINES (LIST_BOTTOM-LIST_TOP) +#define LIST_LINES (LIST_BOTTOM - LIST_TOP) #define LIST_COLS COLS -#define NAMEPOS 2 -#define EMAILPOS opt_get_int(INT_EMAILPOS) -#define EXTRAPOS opt_get_int(INT_EXTRAPOS) - -#define NAMELEN (EMAILPOS - NAMEPOS - 1) -#define EMAILLEN (EXTRAPOS - EMAILPOS - 1) -#define EXTRALEN (COLS - EXTRAPOS) - #define LAST_LIST_ITEM (first_list_item + LIST_LINES - 1) #endif diff --git a/misc.c b/misc.c index 01bdd54..f72475d 100644 --- a/misc.c +++ b/misc.c @@ -64,6 +64,19 @@ strtrim(char *s) return s; } +int +is_number(char *p) +{ + if(!p || !*p || (*p == '-' && !*++p)) + return 0; + + for(; *p; p++) + if(!isdigit(*p)) + return 0; + + return 1; +} + #ifdef HAVE_CONFIG_H # include "config.h" diff --git a/misc.h b/misc.h index d01691b..fadd0ae 100644 --- a/misc.h +++ b/misc.h @@ -16,6 +16,8 @@ char *strupper(char *str); char *strlower(char *str); char *strtrim(char *); +int is_number(char *s); + char *strdup_printf(const char *format, ... ); char *strconcat(const char *str, ...); diff --git a/options.c b/options.c index 16fb365..66a982b 100644 --- a/options.c +++ b/options.c @@ -54,7 +54,7 @@ static struct option abook_vars[] = { { "extra_column", OT_STR, STR_EXTRA_COLUMN, UL "phone" }, { "extra_alternative", OT_STR, STR_EXTRA_ALTERNATIVE, UL "-1" }, { "extrapos", OT_INT, INT_EXTRAPOS, 65 }, - + { "index_format", OT_STR, STR_INDEX_FORMAT, UL " {name:22} {email:40} {phone:12|workphone|mobile}" }, { "mutt_command", OT_STR, STR_MUTT_COMMAND, UL "mutt" }, { "mutt_return_all_emails", OT_BOOL, BOOL_MUTT_RETURN_ALL_EMAILS, TRUE }, diff --git a/options.h b/options.h index c1e02ec..138efc2 100644 --- a/options.h +++ b/options.h @@ -46,6 +46,7 @@ enum int_opts { enum str_opts { STR_EXTRA_COLUMN, STR_EXTRA_ALTERNATIVE, + STR_INDEX_FORMAT, STR_MUTT_COMMAND, STR_PRINT_COMMAND, STR_WWW_COMMAND, diff --git a/sample.abookrc b/sample.abookrc index b324f92..3dac32c 100644 --- a/sample.abookrc +++ b/sample.abookrc @@ -73,27 +73,14 @@ set preserve_fields=standard # Show all email addresses in list set show_all_emails=true -# Screen column for email field to start -set emailpos=25 - -# Field to be used in the extra column -set extra_column=phone -# frequently used values: -# -1 disabled -# phone Home Phone -# workphone Work Phone -# fax Fax -# mobile Mobile Phone -# nick Nick / Alias -# url URL - -# Specify an alternative field to be displayed in the extra -# column if there is no data for the field specified in -# extra_column for a particular item. -set extra_alternative=-1 - -# Screen column for the extra field to start -set extrapos=65 +# Format of an entry's line in the main abook screen +# +# The below example displays: +# * the content of the 'name' field (with a maximum of 22 characters) +# * the first of the 'phone', 'workphone' or 'mobile' fields +# happening not to be empty (right aligned within 12 characters) +# * the 'anniversary' field, with no length limit +set index_format=" {name:25} {phone:-12|workphone|mobile} {anniversary}" # Command used to start mutt set mutt_command=mutt -- 2.39.2
%s