3 * $Id: edit.c,v 1.55 2006/09/06 02:46:44 cduval Exp $
5 * by JH <jheinonen@users.sourceforge.net>
7 * Copyright (C) Jaakko Heinonen
14 #include "abook_curses.h"
29 * some extern variables
32 extern int views_count
;
38 editor_tab(const int tab
)
41 int x_pos
= 2; /* current x pos */
44 mvwhline(editw
, TABLINE
+ 1, 0, UI_HLINE_CHAR
, EDITW_COLS
);
46 for(i
= 0; i
< views_count
; i
++) {
47 view_info(i
, &tab_name
, NULL
);
48 int width
= strwidth(tab_name
) + 5;
50 if(x_pos
+ width
+ 1 > EDITW_COLS
) {
51 statusline_addstr(_("Tab name too wide for screen"));
55 mvwaddch(editw
, TABLINE
+ 1, x_pos
, UI_TEE_CHAR
);
56 mvwaddch(editw
, TABLINE
+ 1, x_pos
+ width
- 2, UI_TEE_CHAR
);
58 mvwaddch(editw
, TABLINE
, x_pos
, UI_ULCORNER_CHAR
);
59 mvwaddch(editw
, TABLINE
, x_pos
+ 1, UI_LBOXLINE_CHAR
);
60 mvwaddstr(editw
, TABLINE
, x_pos
+ 2, tab_name
);
61 mvwaddch(editw
, TABLINE
, x_pos
+ width
- 3, UI_RBOXLINE_CHAR
);
62 mvwaddch(editw
, TABLINE
, x_pos
+ width
- 2, UI_URCORNER_CHAR
);
65 mvwaddch(editw
, TABLINE
+ 1, x_pos
, UI_LRCORNER_CHAR
);
66 for(j
= 0; j
< width
- 3; j
++)
68 TABLINE
+ 1, x_pos
+ j
+ 1, " ");
69 mvwaddch(editw
, TABLINE
+ 1, x_pos
+ width
- 2,
77 get_first_email(char *str
, int item
)
79 char *tmp
, *emails
= db_email_get(item
);
86 strncpy(str
, emails
, MAX_EMAIL_LEN
);
88 if( (tmp
= strchr(str
, ',')) )
91 str
[MAX_EMAIL_LEN
- 1] = 0;
94 /* This only rolls emails from the 'email' field, not emails from any
95 * field of type FIELD_EMAILS.
96 * TODO: expand to ask for which field to roll if several are present? */
98 roll_emails(int item
, enum rotate_dir dir
)
100 abook_list
*emails
= csv_to_abook_list(db_fget(item
, EMAIL
));
105 free(db_fget(item
, EMAIL
));
106 abook_list_rotate(&emails
, dir
);
107 db_fput(item
, EMAIL
, abook_list_to_csv(emails
));
108 abook_list_free(&emails
);
115 editw
= newwin(EDITW_LINES
, EDITW_COLS
, EDITW_TOP
, EDITW_X
);
116 notimeout(editw
, TRUE
); /* handling of escape key */
118 refresh_statusline();
128 edit_undo(int item
, int mode
)
130 static list_item backup
= NULL
;
131 static int backed_up_item
= -1;
145 backup
= item_create();
146 item_duplicate(backup
, db_item_get(item
));
147 backed_up_item
= item
;
151 item_empty(db_item_get(backed_up_item
));
152 item_copy(db_item_get(backed_up_item
), backup
);
154 return backed_up_item
;
166 edit_undo(-1, CLEAR_UNDO
);
172 print_editor_header(int item
)
175 char email
[MAX_EMAIL_LEN
];
177 if((header
= xmalloc(EDITW_COLS
)) == NULL
)
180 get_first_email(email
, item
);
183 snprintf(header
, EDITW_COLS
, "%s <%s>",
187 snprintf(header
, EDITW_COLS
, "%s", db_name_get(item
));
189 mvwaddstr(editw
, 0, (EDITW_COLS
- strwidth(header
)) / 2, header
);
195 editor_print_data(int tab
, int item
)
199 abook_field_list
*cur
;
202 view_info(tab
, NULL
, &cur
);
204 for(; cur
; cur
= cur
->next
) {
212 mvwprintw(editw
, y
, FIELDS_START_X
, "%c - ",
213 (j
< 10) ? '0' + j
: 'A' + j
- 10);
214 mvwaddnstr(editw
, y
, FIELDS_START_X
+ 4, cur
->field
->name
,
215 bytes2width(cur
->field
->name
,
216 FIELDNAME_MAX_WIDTH
));
217 mvwaddch(editw
, y
, TAB_COLON_POS
, ':');
219 if((cur
->field
->type
== FIELD_EMAILS
) ||
220 (cur
->field
->type
== FIELD_LIST
)) {
221 abook_list
*emails
, *e
;
223 find_field_number(cur
->field
->key
, &nb
);
224 emails
= csv_to_abook_list(db_fget_byid(item
, nb
));
226 for(e
= emails
; e
; e
= e
->next
) {
228 mvwaddnstr(editw
, y
+ 1, TAB_COLON_POS
+ 2,
232 mvwaddch(editw
, y
+ 1, TAB_COLON_POS
,
236 mvwaddch(editw
, y
+ 2, TAB_COLON_POS
,
238 mvwhline(editw
, y
+ 2, TAB_COLON_POS
+ 1,
240 EDITW_COLS
- TAB_COLON_POS
- 2);
242 abook_list_free(&emails
);
243 } else if(cur
->field
->type
== FIELD_DATE
) {
244 int day
, month
, year
;
247 find_field_number(cur
->field
->key
, &nb
);
248 if((str
= db_fget_byid(item
, nb
)) != NULL
)
249 strncpy(buf
, str
, sizeof(buf
));
251 if(str
&& parse_date_string(buf
, &day
, &month
, &year
)) {
253 str
= strdup_printf("%04d-%02d-%02d",
256 str
= strdup_printf("--%02d-%02d",
258 mvwaddnstr(editw
, y
, TAB_COLON_POS
+ 2, str
,
259 bytes2width(str
, FIELD_MAX_WIDTH
));
263 find_field_number(cur
->field
->key
, &nb
);
264 str
= safe_str(db_fget_byid(item
, nb
));
265 mvwaddnstr(editw
, y
, TAB_COLON_POS
+ 2, str
,
266 bytes2width(str
, FIELD_MAX_WIDTH
));
274 * function: change_field
278 * message to display as a prompt
280 * a pointer to a pointer which will point a new string. if the latter
281 * pointer != NULL it will be freed (if user doesn't cancel)
283 * maximum length of field to read from user
286 * a nonzero value if user has cancelled and zero if user has typed a
290 change_field(char *msg
, char **field
, size_t max_len
)
297 *field
= ui_readline(msg
, old
, max_len
- 1, 0);
309 refresh_statusline();
315 change_name_field(char *msg
, char **field
, size_t max_len
)
320 tmp
= xstrdup(*field
);
321 ret
= change_field(msg
, field
, max_len
);
323 if(*field
== NULL
|| ! **field
) {
325 *field
= xstrdup(tmp
);
334 fix_email_str(char *str
)
337 *str
= *str
== ',' ? '_' : *str
;
341 edit_list(int item
, int nb
, int isemail
)
343 char *field
, *msg
, *keys
;
344 abook_list
*list
, *e
;
345 int choice
= 1, elem_count
;
347 list
= csv_to_abook_list(db_fget_byid(item
, nb
));
349 for(e
= list
, elem_count
= 0; e
; e
= e
->next
, elem_count
++)
353 keys
= xstrndup(S_("keybindings_new_123456789|n123456789"),
355 msg
= strdup_printf(_("Choose %s to modify (<1>%s%c%s%s."),
356 isemail
? _("email") : _("item"),
357 (elem_count
> 1) ? "-<" : "",
358 (elem_count
> 1) ? '0' + elem_count
: ')',
359 (elem_count
> 1) ? ">)" : "",
360 (elem_count
< MAX_LIST_ITEMS
) ?
363 choice
= statusline_askchoice(
366 (elem_count
< MAX_LIST_ITEMS
) ? 1 : 2
375 field
= (choice
> 1) ?
376 xstrdup(abook_list_get(list
, choice
- 2)->data
) :
379 if(change_field(isemail
? _("E-mail: ") : _("Item: "),
380 &field
, MAX_EMAIL_LEN
))
381 return; /* user cancelled ( C-g ) */
383 /* TODO if list item contains commas, should use quotes instead */
385 fix_email_str(field
);
388 abook_list_append(&list
, field
);
390 abook_list_replace(&list
, choice
- 2, field
);
395 field
= abook_list_to_csv(list
);
396 db_fput_byid(item
, nb
, field
? field
: xstrdup(""));
397 abook_list_free(&list
);
400 static int is_valid_date(const int day
, const int month
, const int year
)
403 int month_length
[13] =
404 { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
409 if ((!(year
% 4)) && ((year
% 100) || !(year
% 400)))
410 month_length
[2] = 29;
412 if (month
< 1 || month
> 12)
414 else if (day
< 1 || day
> month_length
[month
])
416 else if (year
< 0) /* we don't accept negative year numbers */
423 parse_date_string(char *s
, int *day
, int *month
, int *year
)
427 assert(s
&& day
&& month
&& year
);
429 if(*s
== '-' && *s
++ == '-') { /* omitted year */
439 } else if(*s
== '-') {
444 case 1: *year
= safe_atoi(p
); break;
445 case 2: *month
= safe_atoi(p
); break;
457 return is_valid_date(*day
, *month
, *year
);
461 edit_date(int item
, int nb
)
463 int i
, date
[3], old
= FALSE
;
464 char buf
[12], *s
= db_fget_byid(item
, nb
);
465 char *field
[] = { N_("Day: "), N_("Month: "), N_("Year (optional): ") };
468 strncpy(buf
, s
, sizeof(buf
));
469 old
= parse_date_string(buf
, &date
[0], &date
[1], &date
[2]);
472 for(i
= 0; i
< 3; i
++) {
473 s
= (old
&& date
[i
]) ? strdup_printf("%d", date
[i
]) : NULL
;
474 if(change_field(gettext(field
[i
]), &s
, 5))
475 return; /* user aborted with ^G */
477 date
[i
] = (s
&& is_number(s
)) ? atoi(s
) : 0;
481 case 0: db_fput_byid(item
, nb
, NULL
); /*delete*/
482 case 1: /* fall through */ return;
488 /* ISO 8601 date, of the YYYY-MM-DD or --MM-DD format */
489 if(is_valid_date(date
[0], date
[1], date
[2])) {
491 s
= strdup_printf("%04d-%02d-%02d",
492 date
[2], date
[1], date
[0]);
494 s
= strdup_printf("--%02d-%02d", date
[1], date
[0]);
496 db_fput_byid(item
, nb
, xstrdup(s
));
498 statusline_msg(_("Invalid date"));
501 /* input range: 1-9A-Z
502 * output range: 0-34 */
504 key_to_field_number(char c
)
518 edit_field(int tab
, char c
, int item_number
)
520 int i
= 0, number
, idx
;
525 if((number
= key_to_field_number(c
)) < 0)
528 edit_undo(item_number
, BACKUP_ITEM
);
530 view_info(tab
, NULL
, &f
);
543 find_field_number(f
->field
->key
, &idx
);
545 switch(f
->field
->type
) {
547 msg
= strdup_printf("%s: ", f
->field
->name
);
548 item
= db_item_get(item_number
);
549 if(strcmp(f
->field
->key
, "name") == 0)
550 change_name_field(msg
,&item
[idx
],MAX_FIELD_LEN
);
552 change_field(msg
,&item
[idx
],MAX_FIELD_LEN
);
556 edit_list(item_number
, idx
, 0);
559 edit_list(item_number
, idx
, 1);
562 edit_date(item_number
, idx
);
572 static int tab
= 0; /* first tab */
576 headerline(gettext(EDITOR_HELPLINE
));
577 refresh_statusline();
578 print_editor_header(item
);
580 editor_print_data(tab
, item
);
581 wmove(editw
, EDITW_LINES
- 1, EDITW_COLS
- 1);
588 statusline_addstr("ESC-");
592 /* Escaped bindings */
594 case 'r': roll_emails(item
, ROTATE_RIGHT
); break;
601 /* No uppercase nor numeric key should be used in this menu,
602 * as they are reserved for field selection */
605 case KEY_LEFT
: tab
= tab
== 0 ? views_count
- 1 : tab
- 1;
608 case KEY_RIGHT
: tab
= tab
== views_count
- 1 ? 0 : tab
+ 1;
612 case 'k': if(is_valid_item(item
- 1)) item
--; break;
615 case 'j': if(is_valid_item(item
+ 1)) item
++; break;
616 case 'r': roll_emails(item
, ROTATE_LEFT
); break;
617 case '?': display_help(HELP_EDITOR
); break;
618 case 'u': item
= edit_undo(item
, RESTORE_ITEM
); break;
619 case 'm': launch_mutt(item
); clearok(stdscr
, 1); break;
620 case 'v': launch_wwwbrowser(item
); clearok(stdscr
, 1); break;
621 case 12 : clearok(stdscr
, 1); break; /* ^L (refresh screen) */
623 default: edit_field(tab
, c
, item
);
633 if(list_get_curitem() < 0)
636 item
= list_get_curitem();
641 while((item
= edit_loop(item
)) >= 0)
642 list_set_curitem(item
); /* this is not very clean way to go */
651 list_item item
= item_create();
653 change_field(_("Name: "), &field
, MAX_FIELD_LEN
);
658 item_fput(item
, NAME
, field
);
660 add_item2database(item
);
663 list_set_curitem(last_item());
665 edit_item(last_item());