]> git.deb.at Git - pkg/abook.git/blob - edit.c
Imported Upstream version 0.5.6+cvs1
[pkg/abook.git] / edit.c
1
2 /*
3  * $Id: edit.c,v 1.48 2006/08/07 19:20:26 cduval Exp $
4  *
5  * by JH <jheinonen@users.sourceforge.net>
6  *
7  * Copyright (C) Jaakko Heinonen
8  */
9
10 #include <string.h>
11 #include <stdlib.h>
12 #include <ctype.h>
13 #include <assert.h>
14 #include "abook_curses.h"
15 #include "ui.h"
16 #include "abook.h"
17 #include "database.h"
18 #include "gettext.h"
19 #include "list.h"
20 #include "edit.h"
21 #include "misc.h"
22 #include "views.h"
23 #include "xmalloc.h"
24 #ifdef HAVE_CONFIG_H
25 #       include "config.h"
26 #endif
27
28 /*
29  * some extern variables
30  */
31
32 extern int views_count;
33
34 WINDOW *editw;
35
36
37 static void
38 editor_tab(const int tab)
39 {
40         int i, j;
41         int x_pos = 2; /* current x pos */
42         char *tab_name;
43
44         mvwhline(editw, TABLINE + 1, 0, UI_HLINE_CHAR, EDITW_COLS);
45
46         for(i = 0; i < views_count; i++) {
47                 view_info(i, &tab_name, NULL);
48                 int width = strwidth(tab_name) + 5;
49
50                 if(x_pos + width + 1 > EDITW_COLS) {
51                         statusline_addstr(_("Tab name too wide for screen"));
52                         break;
53                 }
54
55                 mvwaddch(editw,  TABLINE + 1, x_pos,  UI_TEE_CHAR);
56                 mvwaddch(editw,  TABLINE + 1, x_pos + width - 2, UI_TEE_CHAR);
57
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);
63
64                 if(i == tab) {
65                         mvwaddch(editw,  TABLINE + 1, x_pos, UI_LRCORNER_CHAR);
66                         for(j = 0; j < width - 3; j++)
67                                 mvwaddstr(editw,
68                                         TABLINE + 1, x_pos + j + 1, " ");
69                         mvwaddch(editw,  TABLINE + 1, x_pos + width - 2,
70                                 UI_LLCORNER_CHAR);
71                 }
72                 x_pos += width;
73         }
74 }
75
76 void
77 get_first_email(char *str, int item)
78 {
79         char *tmp, *emails = db_email_get(item);
80
81         if(!*emails) {
82                 *str = 0;
83                 return;
84         }
85
86         strncpy(str, emails, MAX_EMAIL_LEN);
87         free(emails);
88         if( (tmp = strchr(str, ',')) )
89                 *tmp = 0;
90         else
91                 str[MAX_EMAIL_LEN - 1] = 0;
92 }
93
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? */
97 static void
98 roll_emails(int item, enum rotate_dir dir)
99 {
100         abook_list *emails = csv_to_abook_list(db_fget(item, EMAIL));
101
102         if(!emails)
103                 return;
104
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);
109 }
110
111 static void
112 init_editor()
113 {
114         clear();
115         editw = newwin(EDITW_LINES, EDITW_COLS, EDITW_TOP, EDITW_X);
116         notimeout(editw, TRUE); /* handling of escape key */
117
118         refresh_statusline();
119 }
120
121 enum {
122         BACKUP_ITEM,
123         RESTORE_ITEM,
124         CLEAR_UNDO
125 };
126
127 static int
128 edit_undo(int item, int mode)
129 {
130         static list_item backup = NULL;
131         static int backed_up_item = -1;
132
133         switch(mode) {
134                 case CLEAR_UNDO:
135                         if(backup) {
136                                 item_empty(backup);
137                                 item_free(&backup);
138                         }
139                         break;
140                 case BACKUP_ITEM:
141                         if(backup) {
142                                 item_empty(backup);
143                                 item_free(&backup);
144                         }
145                         backup = item_create();
146                         item_duplicate(backup, db_item_get(item));
147                         backed_up_item = item;
148                         break;
149                 case RESTORE_ITEM:
150                         if(backup) {
151                                 item_empty(db_item_get(backed_up_item));
152                                 item_copy(db_item_get(backed_up_item), backup);
153                                 item_free(&backup);
154                                 return backed_up_item;
155                         }
156                         break;
157                 default:
158                         assert(0);
159         }
160         return item;
161 }
162
163 static void
164 close_editor()
165 {
166         edit_undo(-1, CLEAR_UNDO);
167         delwin(editw);
168         refresh_screen();
169 }
170
171 static void
172 print_editor_header(int item)
173 {
174         char *header;
175         char email[MAX_EMAIL_LEN];
176
177         if((header = xmalloc(EDITW_COLS)) == NULL)
178                 return;
179
180         get_first_email(email, item);
181
182         if(*email)
183                 snprintf(header, EDITW_COLS, "%s <%s>",
184                                 db_name_get(item),
185                                 email);
186         else
187                 snprintf(header, EDITW_COLS, "%s", db_name_get(item));
188
189         mvwaddstr(editw, 0, (EDITW_COLS - strwidth(header)) / 2, header);
190
191         free(header);
192 }
193
194 static void
195 editor_print_data(int tab, int item)
196 {
197         int j = 1, nb;
198         int y, x;
199         abook_field_list *cur;
200         char *str;
201
202         view_info(tab, NULL, &cur);
203
204         for(; cur; cur = cur->next) {
205
206                 if(j > 1) {
207                         getyx(editw, y, x);
208                         y++;
209                 } else
210                         y = FIELDS_START_Y;
211
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, ':');
218
219                 if((cur->field->type == FIELD_EMAILS) ||
220                                 (cur->field->type == FIELD_LIST)) {
221                         abook_list *emails, *e;
222                         
223                         find_field_number(cur->field->key, &nb);
224                         emails = csv_to_abook_list(db_fget_byid(item, nb));
225
226                         for(e = emails; e; e = e->next) {
227                                 getyx(editw, y, x);
228                                 mvwaddnstr(editw, y + 1, TAB_COLON_POS + 2,
229                                                 e->data,
230                                                 bytes2width(e->data,
231                                                         FIELD_MAX_WIDTH));
232                                 mvwaddch(editw, y + 1, TAB_COLON_POS,
233                                                 UI_VLINE_CHAR);
234                         }
235                         if(emails) {
236                                 mvwaddch(editw, y + 2, TAB_COLON_POS,
237                                                 UI_LLCORNER_CHAR);
238                                 mvwhline(editw, y + 2, TAB_COLON_POS + 1,
239                                                 UI_HLINE_CHAR,
240                                                 EDITW_COLS - TAB_COLON_POS - 2);
241                         }
242                         abook_list_free(&emails);
243                 } else {
244                         find_field_number(cur->field->key, &nb);
245                         str = safe_str(db_fget_byid(item, nb));
246                         mvwaddnstr(editw, y, TAB_COLON_POS + 2, str,
247                                 bytes2width(str, FIELD_MAX_WIDTH));
248                 }
249
250                 j++;
251         }
252 }
253
254 /*
255  * function: change_field
256  *
257  * parameters:
258  *  (char *msg)
259  *   message to display as a prompt
260  *  (char **field)
261  *   a pointer to a pointer which will point a new string. if the latter
262  *   pointer != NULL it will be freed (if user doesn't cancel)
263  *  (size_t max_len)
264  *   maximum length of field to read from user
265  *
266  * returns (int)
267  *  a nonzero value if user has cancelled and zero if user has typed a
268  *  valid string
269  */
270 static int
271 change_field(char *msg, char **field, int max_len)
272 {
273         char *old;
274         int ret = 0;
275
276         old = *field;
277
278         *field = ui_readline(msg, old, max_len - 1, 0);
279
280         if(*field) {
281                 xfree(old);
282                 if(!**field)
283                         xfree(*field);
284         } else {
285                 *field = old;
286                 ret = 1;
287         }
288
289         clear_statusline();
290         refresh_statusline();
291
292         return ret;
293 }
294
295 static int
296 change_name_field(char *msg, char **field, int max_len)
297 {
298         char *tmp;
299         int ret;
300
301         tmp = xstrdup(*field);
302         ret = change_field(msg, field, max_len);
303
304         if(*field == NULL || ! **field) {
305                 xfree(*field);
306                 *field = xstrdup(tmp);
307         }
308
309         xfree(tmp);
310
311         return ret;
312 }
313
314 static void
315 fix_email_str(char *str)
316 {
317         for(; *str; str++)
318                 *str = *str == ',' ? '_' : *str;
319 }
320
321 static void
322 edit_list(int item, int nb, int isemail)
323 {
324         char *field, *msg, *keys;
325         abook_list *list, *e;
326         int choice = 1, elem_count;
327
328         list = csv_to_abook_list(db_fget_byid(item, nb));
329
330         for(e = list, elem_count = 0; e; e = e->next, elem_count++)
331                 ;
332
333         if(elem_count) {
334                 keys = xstrndup(S_("keybindings_new_123456789|n123456789"),
335                                 elem_count + 1);
336                 msg = strdup_printf(_("Choose %s to modify (<1>%s%c%s%s."),
337                                 isemail ? _("email") : _("item"),
338                                 (elem_count > 1) ? "-<" : "",
339                                 (elem_count > 1) ?  '0' + elem_count : ')',
340                                 (elem_count > 1) ? ">)" : "",
341                                 (elem_count < MAX_LIST_ITEMS) ?
342                                         _(" or <n>ew") : ""
343                                 );
344                 choice = statusline_askchoice(
345                                 msg,
346                                 keys,
347                                 (elem_count < MAX_LIST_ITEMS) ? 1 : 2
348                                 );
349                 free(keys);
350                 free(msg);
351         }
352
353         if(choice == 0)
354                 return;
355
356         field = (choice > 1) ?
357                 xstrdup(abook_list_get(list, choice - 2)->data) :
358                 NULL;
359
360         if(change_field(isemail ? _("E-mail: ") : _("Item: "),
361                                 &field, MAX_EMAIL_LEN))
362                 return; /* user cancelled ( C-g ) */
363
364         /* TODO if list item contains commas, sjould use quotes instead */
365         if(field)
366                 fix_email_str(field);
367
368         if(choice == 1)
369                 abook_list_append(&list, field);
370         else
371                 abook_list_replace(&list, choice - 2, field);
372
373         if(field)
374                 xfree(field);
375
376         field = abook_list_to_csv(list);
377         db_fput_byid(item, nb, field ? field : xstrdup(""));
378         abook_list_free(&list);
379 }
380
381
382 /* input range: 1-9A-Z
383  * output range: 0-34 */
384 static int
385 key_to_field_number(char c)
386 {
387         int n = c - '1';
388         if(n >= 0 && n < 9)
389                 return n;
390
391         n = c - 'A' + 9;
392         if(n > 8 && n < 35)
393                 return n;
394
395         return -1;
396 }
397
398 static void
399 edit_field(int tab, char c, int item_number)
400 {
401         int i = 0, number, idx;
402         char *msg;
403         abook_field_list *f;
404         list_item item;
405
406         if((number = key_to_field_number(c)) < 0)
407                 return;
408
409         edit_undo(item_number, BACKUP_ITEM);
410
411         view_info(tab, NULL, &f);
412
413         while(1) {
414                 if(!f)
415                         return;
416
417                 if(i == number)
418                         break;
419
420                 f = f->next;
421                 i++;
422         }
423
424         find_field_number(f->field->key, &idx);
425         
426         switch(f->field->type) {
427                 case FIELD_STRING:
428                         msg = strdup_printf("%s: ", f->field->name);
429                         item = db_item_get(item_number);
430                         if(strcmp(f->field->key, "name") == 0)
431                                 change_name_field(msg,&item[idx],MAX_FIELD_LEN);
432                         else
433                                 change_field(msg,&item[idx],MAX_FIELD_LEN);
434                         free(msg);
435                         break;
436                 case FIELD_LIST:
437                         edit_list(item_number, idx, 0);
438                         break;
439                 case FIELD_EMAILS:
440                         edit_list(item_number, idx, 1);
441                         break;
442                 case FIELD_DAY:
443                         statusline_msg(_("sorry, input for this field type is "
444                                                 "not yet implemented"));
445                         return;
446                 default:
447                         assert(0);
448         }
449 }
450
451 static int
452 edit_loop(int item)
453 {
454         static int tab = 0; /* first tab */
455         int c;
456
457         werase(editw);
458         headerline(gettext(EDITOR_HELPLINE));
459         refresh_statusline();
460         print_editor_header(item);
461         editor_tab(tab);
462         editor_print_data(tab, item);
463         wmove(editw, EDITW_LINES - 1, EDITW_COLS - 1);
464
465         refresh();
466         wrefresh(editw);
467
468         c = getch();
469         if(c == '\033') {
470                 statusline_addstr("ESC-");
471                 c = getch();
472                 clear_statusline();
473
474                 /* Escaped bindings */
475                 switch(c) {
476                         case 'r': roll_emails(item, ROTATE_RIGHT); break;
477                         default: break;
478                 }
479
480                 return item;
481         }
482
483         /* No uppercase nor numeric key should be used in this menu,
484          * as they are reserved for field selection */
485         switch(c) {
486                 case 'h':
487                 case KEY_LEFT: tab = tab == 0 ? views_count - 1 : tab - 1;
488                                break;
489                 case 'l':
490                 case KEY_RIGHT: tab = tab == views_count - 1 ? 0 : tab + 1;
491                                 break;
492                 case KEY_UP:
493                 case '<':
494                 case 'k': if(is_valid_item(item - 1)) item--; break;
495                 case KEY_DOWN:
496                 case '>':
497                 case 'j': if(is_valid_item(item + 1)) item++; break;
498                 case 'r': roll_emails(item, ROTATE_LEFT); break;
499                 case '?': display_help(HELP_EDITOR); break;
500                 case 'u': item = edit_undo(item, RESTORE_ITEM); break;
501                 case 'm': launch_mutt(item); clearok(stdscr, 1); break;
502                 case 'v': launch_wwwbrowser(item); clearok(stdscr, 1); break;
503                 case 12 : clearok(stdscr, 1); break; /* ^L (refresh screen) */
504                 case 'q': return -1;
505                 default: edit_field(tab, c, item);
506         }
507
508         return item;
509 }
510
511 void
512 edit_item(int item)
513 {
514         if(item < 0) {
515                 if(list_get_curitem() < 0)
516                         return;
517                 else
518                         item = list_get_curitem();
519         }
520
521         init_editor();
522
523         while((item = edit_loop(item)) >= 0)
524                 list_set_curitem(item); /* this is not very clean way to go */
525
526         close_editor();
527 }
528
529 void
530 add_item()
531 {
532         char *field = NULL;
533         list_item item = item_create();
534
535         change_field(_("Name: "), &field, MAX_FIELD_LEN);
536
537         if( field == NULL )
538                 return;
539
540         item_fput(item, NAME, field);
541
542         add_item2database(item);
543         item_free(&item);
544
545         list_set_curitem(last_item());
546
547         edit_item(last_item());
548 }
549