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