X-Git-Url: https://git.deb.at/?p=pkg%2Fabook.git;a=blobdiff_plain;f=edit.c;h=9a76c8a8970aadda843bd1b7ab7638f3e2c84ec1;hp=f5b58e24c428ff4780887e398ce5a23251b0a936;hb=HEAD;hpb=7751b3edc145646e6b55e2fda82eb5619fbe0074 diff --git a/edit.c b/edit.c index f5b58e2..9a76c8a 100644 --- a/edit.c +++ b/edit.c @@ -1,6 +1,6 @@ /* - * $Id: edit.c,v 1.48 2006/08/07 19:20:26 cduval Exp $ + * $Id$ * * by JH * @@ -21,9 +21,16 @@ #include "misc.h" #include "views.h" #include "xmalloc.h" +#include "color.h" #ifdef HAVE_CONFIG_H # include "config.h" #endif +#if defined(HAVE_LOCALE_H) && defined(HAVE_SETLOCALE) +# include +#endif + + +static void locale_date(char *str, size_t str_len, int year, int month, int day); /* * some extern variables @@ -41,6 +48,7 @@ editor_tab(const int tab) int x_pos = 2; /* current x pos */ char *tab_name; + wattrset(editw, COLOR_PAIR(CP_TAB_BORDER)); mvwhline(editw, TABLINE + 1, 0, UI_HLINE_CHAR, EDITW_COLS); for(i = 0; i < views_count; i++) { @@ -57,7 +65,9 @@ editor_tab(const int tab) mvwaddch(editw, TABLINE, x_pos, UI_ULCORNER_CHAR); mvwaddch(editw, TABLINE, x_pos + 1, UI_LBOXLINE_CHAR); + wattrset(editw, COLOR_PAIR(CP_TAB_LABEL)); mvwaddstr(editw, TABLINE, x_pos + 2, tab_name); + wattrset(editw, COLOR_PAIR(CP_TAB_BORDER)); mvwaddch(editw, TABLINE, x_pos + width - 3, UI_RBOXLINE_CHAR); mvwaddch(editw, TABLINE, x_pos + width - 2, UI_URCORNER_CHAR); @@ -94,7 +104,7 @@ get_first_email(char *str, int item) /* This only rolls emails from the 'email' field, not emails from any * field of type FIELD_EMAILS. * TODO: expand to ask for which field to roll if several are present? */ -static void +void roll_emails(int item, enum rotate_dir dir) { abook_list *emails = csv_to_abook_list(db_fget(item, EMAIL)); @@ -186,6 +196,7 @@ print_editor_header(int item) else snprintf(header, EDITW_COLS, "%s", db_name_get(item)); + wattrset(editw, COLOR_PAIR(CP_TAB_LABEL)); mvwaddstr(editw, 0, (EDITW_COLS - strwidth(header)) / 2, header); free(header); @@ -209,6 +220,7 @@ editor_print_data(int tab, int item) } else y = FIELDS_START_Y; + wattrset(editw, COLOR_PAIR(CP_FIELD_NAME)); mvwprintw(editw, y, FIELDS_START_X, "%c - ", (j < 10) ? '0' + j : 'A' + j - 10); mvwaddnstr(editw, y, FIELDS_START_X + 4, cur->field->name, @@ -216,6 +228,7 @@ editor_print_data(int tab, int item) FIELDNAME_MAX_WIDTH)); mvwaddch(editw, y, TAB_COLON_POS, ':'); + wattrset(editw, COLOR_PAIR(CP_FIELD_VALUE)); if((cur->field->type == FIELD_EMAILS) || (cur->field->type == FIELD_LIST)) { abook_list *emails, *e; @@ -240,6 +253,19 @@ editor_print_data(int tab, int item) EDITW_COLS - TAB_COLON_POS - 2); } abook_list_free(&emails); + } else if(cur->field->type == FIELD_DATE) { + int day, month, year; + char buf[64]; + + find_field_number(cur->field->key, &nb); + str = db_fget_byid(item, nb); + + if(parse_date_string(str, &day, &month, &year)) { + /* put locale representation of date in buf */ + locale_date(buf, sizeof(buf), year, month, day); + mvwaddnstr(editw, y, TAB_COLON_POS + 2, buf, + bytes2width(buf, FIELD_MAX_WIDTH)); + } } else { find_field_number(cur->field->key, &nb); str = safe_str(db_fget_byid(item, nb)); @@ -268,7 +294,7 @@ editor_print_data(int tab, int item) * valid string */ static int -change_field(char *msg, char **field, int max_len) +change_field(char *msg, char **field, size_t max_len) { char *old; int ret = 0; @@ -293,7 +319,7 @@ change_field(char *msg, char **field, int max_len) } static int -change_name_field(char *msg, char **field, int max_len) +change_name_field(char *msg, char **field, size_t max_len) { char *tmp; int ret; @@ -361,7 +387,7 @@ edit_list(int item, int nb, int isemail) &field, MAX_EMAIL_LEN)) return; /* user cancelled ( C-g ) */ - /* TODO if list item contains commas, sjould use quotes instead */ + /* TODO if list item contains commas, should use quotes instead */ if(field) fix_email_str(field); @@ -378,6 +404,170 @@ edit_list(int item, int nb, int isemail) abook_list_free(&list); } +/* + * available %-sequences: + * - %y, %Y, %m, %M, %d, %D represent year, month, and day + * (the uppercase version telling to fill with leading zeros + * if necessary) + * - %I for ISO 8601 representation + */ +static size_t +format_date(char *str, size_t str_len, char *fmt, int year, int month, int day) +{ + char *s = str; + size_t len; + + while(*fmt && (s - str + 1 < str_len)) { + if(*fmt != '%') { + *s++ = *fmt++; + continue; + } + + len = str_len - (str - s); + switch(*++fmt) { + case 'y': s += snprintf(s, len, "%d", year); break; + case 'Y': s += snprintf(s, len, "%04d", year); break; + case 'm': s += snprintf(s, len, "%d", month); break; + case 'M': s += snprintf(s, len, "%02d", month); break; + case 'd': s += snprintf(s, len, "%d", day); break; + case 'D': s += snprintf(s, len, "%02d", day); break; + case 'I': s += format_date(s, len, + year ? "%Y-%M-%D" : "--%M-%D", + year, month, day); + break; + case '%': *s++ = '%'; break; + default: *s++ = '%'; *s++ = *fmt; break; + } + fmt++; + } + *s = 0; + return s - str; +} + +/* + * str is a buffer of max length str_len, which, after calling, will + * contain a representation of the given [y, m, d] date using the + * current locale (as defined by LC_TIME). + * + * In the absence of any localization, use an ISO 8601 representation. + */ +static void +locale_date(char *str, size_t str_len, int year, int month, int day) +{ + char *fmt; + +#if defined(HAVE_LOCALE_H) && defined(HAVE_SETLOCALE) + fmt = year ? dcgettext(PACKAGE, "%Y-%M-%D", LC_TIME) : + dcgettext(PACKAGE, "--%M-%D", LC_TIME); +#else + fmt = "%I"; +#endif + format_date(str, str_len, fmt, year, month, day); +} + +static int is_valid_date(const int day, const int month, const int year) +{ + int valid = 1; + int month_length[13] = + { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + + /* + * leap year + */ + if ((!(year % 4)) && ((year % 100) || !(year % 400))) + month_length[2] = 29; + + if (month < 1 || month > 12) + valid = 0; + else if (day < 1 || day > month_length[month]) + valid = 0; + else if (year < 0) /* we don't accept negative year numbers */ + valid = 0; + + return valid; +} + +int +parse_date_string(char *str, int *day, int *month, int *year) +{ + int i = 0; + char buf[12], *s, *p; + + assert(day && month && year); + + if(!str || !*str) + return FALSE; + + p = s = strncpy(buf, str, sizeof(buf)); + + if(*s == '-' && *s++ == '-') { /* omitted year */ + *year = 0; + p = ++s; + i++; + } + + while(*s) { + if(isdigit(*s)) { + s++; + continue; + } else if(*s == '-') { + if(++i > 3) + return FALSE; + *s++ = '\0'; + switch(i) { + case 1: *year = safe_atoi(p); break; + case 2: *month = safe_atoi(p); break; + } + p = s; + } else + return FALSE; + } + + if (i != 2 || !*p) + return FALSE; + + *day = atoi(p); + + return is_valid_date(*day, *month, *year); +} + +static void +edit_date(int item, int nb) +{ + int i, date[3], old; + char *s = db_fget_byid(item, nb); + char *field[] = { N_("Day: "), N_("Month: "), N_("Year (optional): ") }; + + old = parse_date_string(s, &date[0], &date[1], &date[2]); + + for(i = 0; i < 3; i++) { + s = (old && date[i]) ? strdup_printf("%d", date[i]) : NULL; + if(change_field(gettext(field[i]), &s, 5)) + return; /* user aborted with ^G */ + + date[i] = (s && is_number(s)) ? atoi(s) : 0; + + if(!s) { + switch(i) { + case 0: db_fput_byid(item, nb, NULL); /*delete*/ + case 1: /* fall through */ return; + } + } else + xfree(s); + } + + /* ISO 8601 date, of the YYYY-MM-DD or --MM-DD format */ + if(is_valid_date(date[0], date[1], date[2])) { + if(date[2]) + s = strdup_printf("%04d-%02d-%02d", + date[2], date[1], date[0]); + else + s = strdup_printf("--%02d-%02d", date[1], date[0]); + + db_fput_byid(item, nb, xstrdup(s)); + } else + statusline_msg(_("Invalid date")); +} /* input range: 1-9A-Z * output range: 0-34 */ @@ -398,13 +588,14 @@ key_to_field_number(char c) static void edit_field(int tab, char c, int item_number) { + ui_enable_mouse(FALSE); int i = 0, number, idx; char *msg; abook_field_list *f; list_item item; if((number = key_to_field_number(c)) < 0) - return; + goto detachfield; edit_undo(item_number, BACKUP_ITEM); @@ -412,7 +603,7 @@ edit_field(int tab, char c, int item_number) while(1) { if(!f) - return; + goto detachfield; if(i == number) break; @@ -439,13 +630,16 @@ edit_field(int tab, char c, int item_number) case FIELD_EMAILS: edit_list(item_number, idx, 1); break; - case FIELD_DAY: - statusline_msg(_("sorry, input for this field type is " - "not yet implemented")); - return; + case FIELD_DATE: + edit_date(item_number, idx); + goto detachfield; default: assert(0); } + + detachfield: + if(opt_get_bool(BOOL_USE_MOUSE)) + ui_enable_mouse(TRUE); } static int @@ -479,6 +673,53 @@ edit_loop(int item) return item; } + if(c == KEY_MOUSE) { + MEVENT event; + if(getmouse(&event) == OK) { + if(event.bstate & BUTTON1_CLICKED + || event.bstate & BUTTON1_DOUBLE_CLICKED) { + int window_y, window_x; + getbegyx(editw, window_y, window_x); + if(event.y == 0) { + /* if first row is selected, then go back to list */ + return -1; + } else if(event.y == window_y + TABLINE + || event.y == window_y + TABLINE + 1) { + char* tab_name; + int mouse_x = event.x; + int xpos = 2 + 1; /* look at editor_tab() and try out */ + int clicked_tab = 0; + while(clicked_tab < views_count) { + view_info(clicked_tab, &tab_name, NULL); + xpos += strwidth(tab_name) + 5; + /* fprintf(stderr, "trying tab %d\n", clicked_tab); */ + if(xpos >= mouse_x) { + break; /* clicked tab was found */ + } else { + /* try next tab */ + clicked_tab++; + } + } + if(clicked_tab < views_count) { + tab = clicked_tab; + } + } else if(event.y >= window_y + FIELDS_START_Y) { + /* is mouse in field area? */ + int j = 1 + event.y - window_y - FIELDS_START_Y; + /* field numbers start with 1, but if j='0', then char='0' */ + /* so fix this, by adding 1 to j */ + int field_char = (j < 10) ? '0' + j : 'A' + j - 10; + edit_field(tab, field_char, item); + } + } else if(event.bstate & BUTTON4_PRESSED) { + tab = tab == 0 ? views_count - 1 : tab - 1; + } + else if(event.bstate & BUTTON5_PRESSED) { + tab = tab == views_count - 1 ? 0 : tab + 1; + } + return item; + } + } /* No uppercase nor numeric key should be used in this menu, * as they are reserved for field selection */