use automake 1.11, autoconf 2.65
[abook.git] / ui.c
blob6fe2f0f47079ff9692441b62356cff8e416683ef
2 /*
3 * $Id: ui.c,v 1.58 2006/09/06 02:24:33 cduval Exp $
5 * by JH <jheinonen@users.sourceforge.net>
7 * Copyright (C) Jaakko Heinonen
8 */
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <unistd.h>
14 #include <fcntl.h>
15 #include <signal.h>
16 #include <ctype.h>
17 #include "abook.h"
18 #include <assert.h>
19 #include "ui.h"
20 #include "edit.h"
21 #include "database.h"
22 #include "gettext.h"
23 #include "list.h"
24 #include "misc.h"
25 #include "options.h"
26 #include "filter.h"
27 #include "xmalloc.h"
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31 #ifdef HAVE_SYS_IOCTL_H
32 # include <sys/ioctl.h>
33 #endif
35 #include "abook_rl.h"
38 * external variables
41 extern char *datafile;
43 extern bool alternative_datafile;
46 * internal variables
49 static bool ui_initialized = FALSE;
51 static bool should_resize = FALSE;
52 static bool can_resize = FALSE;
54 static WINDOW *top = NULL, *bottom = NULL;
57 static void
58 init_windows()
60 top = newwin(LIST_TOP - 1, COLS, 0, 0);
61 bottom = newwin(LINES - LIST_BOTTOM, COLS, LIST_BOTTOM, 0);
64 static void
65 free_windows()
67 delwin(top);
68 delwin(bottom);
72 #ifdef SIGWINCH
73 static void
74 resize_abook()
76 #ifdef TIOCGWINSZ
77 struct winsize winsz;
79 ioctl(0, TIOCGWINSZ, &winsz);
80 #ifdef DEBUG
81 if(winsz.ws_col >= MIN_COLS && winsz.ws_row >= MIN_LINES) {
82 fprintf(stderr, "Warning: COLS=%d, LINES=%d\n", winsz.ws_col, winsz.ws_row);
84 #endif
86 if(winsz.ws_col >= MIN_COLS && winsz.ws_row >= MIN_LINES) {
87 #ifdef HAVE_RESIZETERM
88 resizeterm(winsz.ws_row, winsz.ws_col);
89 #else
90 COLS = winsz.ws_col;
91 LINES = winsz.ws_row;
92 #endif
95 should_resize = FALSE;
96 close_list(); /* we need to recreate windows */
97 init_list();
98 free_windows();
99 init_windows();
100 refresh_screen();
101 refresh();
102 #endif /* TIOCGWINSZ */
106 static void
107 win_changed(int i)
109 if(can_resize)
110 resize_abook();
111 else
112 should_resize = TRUE;
114 #endif /* SIGWINCH */
118 is_ui_initialized()
120 return ui_initialized;
123 void
124 ui_init_curses()
126 if(!is_ui_initialized())
127 initscr();
128 cbreak();
129 noecho();
130 nonl();
131 intrflush(stdscr, FALSE);
132 keypad(stdscr, TRUE);
136 init_ui()
138 ui_init_curses();
139 #ifdef DEBUG
140 fprintf(stderr, "init_abook():\n");
141 fprintf(stderr, " COLS = %d, LINES = %d\n", COLS, LINES);
142 #endif
143 if( LINES < MIN_LINES || COLS < MIN_COLS ) {
144 clear(); refresh(); endwin();
145 fprintf(stderr, _("Your terminal size is %dx%d\n"), COLS, LINES);
146 fprintf(stderr, _("Terminal is too small. Minimum terminal "
147 "size for abook is "
148 "%dx%d\n"), MIN_COLS, MIN_LINES);
149 return 1;
152 init_list();
153 init_windows();
155 ui_initialized = TRUE;
157 #ifdef SIGWINCH
158 signal(SIGWINCH, win_changed);
159 #endif
161 return 0;
164 void
165 close_ui()
167 close_list();
168 free_windows();
169 clear();
170 refresh();
171 endwin();
173 ui_initialized = FALSE;
177 void
178 headerline(const char *str)
180 werase(top);
182 mvwhline(top, 1, 0, UI_HLINE_CHAR, COLS);
184 mvwprintw(top, 0, 0, "%s | %s", PACKAGE " " VERSION, str);
186 refresh();
187 wrefresh(top);
191 void
192 refresh_screen()
194 #ifdef SIGWINCH
195 if(should_resize) {
196 resize_abook();
197 return;
199 #endif
200 clear();
202 refresh_statusline();
203 headerline(gettext(MAIN_HELPLINE));
204 list_headerline();
206 refresh_list();
211 statusline_msg(const char *msg)
213 int c;
215 clear_statusline();
216 statusline_addstr(msg);
217 c = getch();
218 #ifdef DEBUG
219 fprintf(stderr, "statusline_msg(\"%s\")\n", msg);
220 #endif
221 clear_statusline();
223 return c;
226 void
227 statusline_addstr(const char *str)
229 mvwaddstr(bottom, 1, 0, str);
230 refresh();
231 wrefresh(bottom);
234 /* Same as statusline_addstr(), but hilight "<str>" sequences if the terminal
235 * supports it */
236 static void
237 statusline_addhlstr(const char *str)
239 #if defined(A_BOLD) && defined(A_NORMAL) && defined(A_DIM)
240 const char *p = str, *start = str;
241 char *tmp;
242 int pos = 0;
244 while(1) {
245 if(!*p || strchr("<>", *p)) {
246 if(p - start > 0) {
247 wattrset(bottom, (*p == '>') ? A_BOLD : A_NORMAL);
248 tmp = xstrndup(start, p - start);
249 mvwaddstr(bottom, 1, pos, tmp);
250 pos += strwidth(tmp);
251 free(tmp);
253 if(*p) {
254 start = p + 1;
256 /* show tag markers */
257 wattrset(bottom, A_DIM);
258 mvwaddch(bottom, 1, pos++, *p);
262 if(!*p) {
263 wattrset(bottom, A_NORMAL);
264 break;
267 p++;
269 #else
270 mvwaddstr(bottom, 1, 0, str);
271 #endif
273 refresh();
274 wrefresh(bottom);
278 statusline_askchoice(const char *msg, const char *choices, short dflt)
280 char *s;
281 int ch;
283 assert((dflt >= 0) && (dflt <= strlen(choices)));
285 if(dflt) {
286 s = strdup_printf("%s [%c]", msg, choices[dflt - 1]);
287 statusline_addhlstr(s);
288 free(s);
289 } else
290 statusline_addhlstr(msg);
292 while(1)
294 ch = tolower(getch());
296 if(ch == 7) /* ctrl+G */
297 return 0;
299 if(dflt && (ch == '\r')) /* default choice */
300 return dflt;
302 if((s = strchr(choices, ch)))
303 return (s - choices + 1);
307 char *
308 ui_readline(const char *prompt, char *s, size_t limit, bool use_completion)
310 int y, x;
311 char *ret;
313 mvwaddstr(bottom, 1, 0, prompt);
315 getyx(bottom, y, x);
317 ret = abook_readline(bottom, y, x, s, use_completion);
319 if(ret) {
320 strtrim(ret);
321 if(strlen(ret) > limit && limit > 0)
322 ret[limit] = '\0';
325 return ret;
329 statusline_ask_boolean(const char *msg, int def)
331 int ret;
332 char *msg2 = strconcat(msg, def ? _(" (Y/n)?") : _(" (y/N)?"), NULL);
333 char ch;
335 statusline_addstr(msg2);
337 free(msg2);
339 ch = tolower(getch());
341 if(ch == *(S_("keybinding for no|n")))
342 ret = FALSE;
343 else if(ch == *(S_("keybinding for yes|y")))
344 ret = TRUE;
345 else
346 ret = def;
348 clear_statusline();
350 return ret;
353 void
354 refresh_statusline()
356 werase(bottom);
358 mvwhline(bottom, 0, 0, UI_HLINE_CHAR, COLS);
360 refresh();
361 wrefresh(bottom);
364 char *
365 ask_filename(const char *prompt)
367 char *buf = NULL;
369 clear_statusline();
371 buf = ui_readline(prompt, NULL, -1, 1);
373 return buf;
376 void
377 clear_statusline()
379 wmove(bottom, 1, 0);
380 wclrtoeol(bottom);
381 wrefresh(bottom);
382 refresh();
386 * help
389 #include "help.h"
391 void
392 display_help(int help)
394 int i;
395 char **tbl;
396 WINDOW *helpw;
398 switch(help) {
399 case HELP_MAIN:
400 tbl = mainhelp;
401 break;
402 case HELP_EDITOR:
403 tbl = editorhelp;
404 break;
405 default:return;
408 helpw = newwin(LINES - 5, COLS - 6, 2, 3);
409 erase();
410 headerline(_("help"));
412 for(i = 0; tbl[i] != NULL; i++) {
413 waddstr(helpw, gettext(tbl[i]));
414 if( (!((i + 1) % (LINES - 8))) ||
415 (tbl[i + 1] == NULL) ) {
416 refresh();
417 wrefresh(helpw);
418 refresh_statusline();
419 if(statusline_msg(_("Press any key to continue..."))
420 == 'q')
421 break;
422 wclear(helpw);
426 clear_statusline();
427 delwin(helpw);
431 * end of help
434 extern char *selected;
436 void
437 get_commands()
439 int ch;
441 for(;;) {
442 can_resize = TRUE; /* it's safe to resize now */
443 if(!opt_get_bool(BOOL_SHOW_CURSOR))
444 hide_cursor();
445 if(should_resize)
446 refresh_screen();
447 ch = getch();
448 if(!opt_get_bool(BOOL_SHOW_CURSOR))
449 show_cursor();
450 can_resize = FALSE; /* it's not safe to resize anymore */
451 switch(ch) {
452 case 'q': return;
453 case 'Q': quit_abook(QUIT_DONTSAVE); break;
454 case 'P': print_stderr(selected_items() ?
455 -1 : list_get_curitem());
456 return;
457 case '?':
458 display_help(HELP_MAIN);
459 refresh_screen();
460 break;
461 case 'a': add_item(); break;
462 case '\r': edit_item(-1); break;
463 case KEY_DC:
464 case 'd':
465 case 'r': ui_remove_items(); break;
466 case 'D': duplicate_item(); break;
467 case 12: refresh_screen(); break;
469 case 'k':
470 case KEY_UP: scroll_up(); break;
471 case 'j':
472 case KEY_DOWN: scroll_down(); break;
473 case 'K':
474 case KEY_PPAGE: page_up(); break;
475 case 'J':
476 case KEY_NPAGE: page_down(); break;
478 case 'g':
479 case KEY_HOME: goto_home(); break;
480 case 'G':
481 case KEY_END: goto_end(); break;
483 case 'w': save_database();
484 break;
485 case 'l': ui_read_database(); break;
486 case 'i': import_database(); break;
487 case 'e': export_database(); break;
488 case 'C': ui_clear_database(); break;
490 case 'o': ui_open_datafile(); break;
492 case 's': sort_by_field("name");break;
493 case 'S': sort_surname(); break;
494 case 'F': sort_by_field(NULL); break;
496 case '/': ui_find(0); break;
497 case '\\': ui_find(1); break;
499 case ' ': if(list_get_curitem() >= 0) {
500 list_invert_curitem_selection();
501 ui_print_number_of_items();
502 refresh_list();
504 break;
505 case '+': select_all();
506 refresh_list();
507 break;
508 case '-': select_none();
509 refresh_list();
510 break;
511 case '*': invert_selection();
512 refresh_list();
513 break;
514 case 'A': move_curitem(MOVE_ITEM_UP);
515 break;
516 case 'Z': move_curitem(MOVE_ITEM_DOWN);
517 break;
519 case 'm': launch_mutt(selected_items() ?
520 -1 : list_get_curitem());
521 refresh_screen();
522 break;
524 case 'p': ui_print_database(); break;
526 case 'v': launch_wwwbrowser(list_get_curitem());
527 refresh_screen();
528 break;
533 void
534 ui_remove_items()
536 if(list_is_empty())
537 return;
539 if(statusline_ask_boolean(_("Remove selected item(s)"), TRUE))
540 remove_selected_items();
542 clear_statusline();
543 refresh_list();
546 void
547 ui_clear_database()
549 if(statusline_ask_boolean(_("Clear WHOLE database"), FALSE)) {
550 close_database();
551 refresh_list();
555 void
556 ui_find(int next)
558 int item = -1;
559 static char findstr[MAX_FIELD_LEN];
560 int search_fields[] = {NAME, EMAIL, NICK, -1};
562 clear_statusline();
564 if(next) {
565 if(!*findstr)
566 return;
567 } else {
568 char *s;
569 s = ui_readline("/", findstr, MAX_FIELD_LEN - 1, 0);
570 refresh_screen();
571 if(s == NULL) {
572 return; /* user cancelled (ctrl-G) */
573 } else {
574 strncpy(findstr, s, MAX_FIELD_LEN);
575 free(s);
579 if( (item = find_item(findstr, list_get_curitem() + !!next,
580 search_fields)) < 0 &&
581 (item = find_item(findstr, 0, search_fields)) >= 0)
582 statusline_addstr(_("Search hit bottom, continuing at top"));
584 if(item >= 0) {
585 list_set_curitem(item);
586 refresh_list();
590 void
591 ui_print_number_of_items()
593 char *str = strdup_printf(" " "|%3d/%3d",
594 selected_items(), db_n_items());
596 mvaddstr(0, COLS-strlen(str), str);
598 free(str);
601 void
602 ui_read_database()
604 char *msg;
606 if(!list_is_empty()) {
607 msg = strdup_printf(_("Your current data will be lost - "
608 "Press '%c' to continue"),
609 *(S_("keybinding for yes|y")));
610 if(!statusline_ask_boolean(msg, FALSE)) {
611 free(msg);
612 return;
614 free(msg);
617 load_database(datafile);
618 refresh_list();
622 void
623 ui_print_database()
625 FILE *handle;
626 char *command = opt_get_str(STR_PRINT_COMMAND);
627 int mode;
629 if(list_is_empty())
630 return;
632 switch(statusline_askchoice(_("Print <a>ll, print <s>elected, or <c>ancel?"), S_("keybindings:all/selected/cancel|asc"), 3)) {
633 case 1:
634 mode = ENUM_ALL;
635 break;
636 case 2:
637 if( !selected_items() ) {
638 statusline_msg(_("No selected items"));
639 return;
641 mode = ENUM_SELECTED;
642 break;
643 default:
644 refresh_screen();
645 return;
648 clear_statusline();
650 if( ! *command || (handle = popen(command, "w")) == NULL)
651 return;
653 fexport("text", handle, mode);
655 pclose(handle);
659 void
660 ui_open_datafile()
662 char *filename;
664 filename = ask_filename(_("File to open: "));
666 if(!filename || ! *filename) {
667 free(filename);
668 refresh_screen();
669 return;
672 if(opt_get_bool(BOOL_AUTOSAVE))
673 save_database();
674 else if(statusline_ask_boolean(_("Save current database"), FALSE))
675 save_database();
677 close_database();
679 load_database(filename);
681 if(list_is_empty()) {
682 statusline_msg(_("Sorry, the specified file appears not to be a valid abook addressbook"));
683 load_database(datafile);
684 } else {
685 free(datafile);
686 datafile = xstrdup(filename);
689 refresh_screen();
690 free(filename);
692 alternative_datafile = TRUE;