5 * by JH <jheinonen@users.sourceforge.net>
7 * Copyright (C) Jaakko Heinonen
33 #ifdef HAVE_SYS_IOCTL_H
34 # include <sys/ioctl.h>
43 extern char *datafile;
45 extern bool alternative_datafile;
51 static bool ui_initialized = FALSE;
53 static bool should_resize = FALSE;
54 static bool can_resize = FALSE;
55 static struct timeval last_click_time;
56 static int double_click_interval = 200; /* maximum time in milliseconds */
58 static WINDOW *top = NULL, *bottom = NULL;
64 top = newwin(LIST_TOP - 1, COLS, 0, 0);
65 bottom = newwin(LINES - LIST_BOTTOM, COLS, LIST_BOTTOM, 0);
83 ioctl(0, TIOCGWINSZ, &winsz);
85 if(winsz.ws_col >= MIN_COLS && winsz.ws_row >= MIN_LINES) {
86 fprintf(stderr, "Warning: COLS=%d, LINES=%d\n", winsz.ws_col, winsz.ws_row);
90 if(winsz.ws_col >= MIN_COLS && winsz.ws_row >= MIN_LINES) {
91 #ifdef HAVE_RESIZETERM
92 resizeterm(winsz.ws_row, winsz.ws_col);
99 should_resize = FALSE;
100 close_list(); /* we need to recreate windows */
106 #endif /* TIOCGWINSZ */
116 should_resize = TRUE;
118 #endif /* SIGWINCH */
124 return ui_initialized;
130 if(!is_ui_initialized())
135 intrflush(stdscr, FALSE);
136 if(opt_get_bool(BOOL_USE_MOUSE)) {
138 timerclear(&last_click_time);
139 ui_enable_mouse(TRUE);
141 keypad(stdscr, TRUE);
142 if(opt_get_bool(BOOL_USE_COLORS)) {
144 use_default_colors();
145 ui_init_color_pairs_user();
150 ui_enable_mouse(bool enabled)
154 mask = BUTTON1_CLICKED | BUTTON4_PRESSED;
155 #if NCURSES_MOUSE_VERSION == 2
156 mask |= BUTTON5_PRESSED;
161 mousemask(mask, NULL);
164 /** Check the time elapsed since last click and tell if it should be
165 * interpreted as a double click
169 struct timeval click_time, click_diff, maxdiff;
170 maxdiff.tv_sec = double_click_interval / 1000;
171 maxdiff.tv_usec = (double_click_interval % 1000)*1000;
172 gettimeofday(&click_time, NULL);
174 timersub(&click_time, &last_click_time, &click_diff);
175 last_click_time = click_time;
176 return !timercmp(&click_diff, &maxdiff, >);
179 #define CHECK_COLOR_NAME(value, name, DEFNAME) \
180 if(!strcmp((name), (value))){ \
184 opt_color_to_color(enum str_opts enum_name)
186 char* name = opt_get_str(enum_name);
187 CHECK_COLOR_NAME(name, "default", COLOR_DEFAULT)
188 else CHECK_COLOR_NAME(name, "black", COLOR_BLACK)
189 else CHECK_COLOR_NAME(name, "red", COLOR_RED)
190 else CHECK_COLOR_NAME(name, "green", COLOR_GREEN)
191 else CHECK_COLOR_NAME(name, "yellow", COLOR_YELLOW)
192 else CHECK_COLOR_NAME(name, "blue", COLOR_BLUE)
193 else CHECK_COLOR_NAME(name, "magenta", COLOR_MAGENTA)
194 else CHECK_COLOR_NAME(name, "cyan", COLOR_CYAN)
195 else CHECK_COLOR_NAME(name, "white", COLOR_WHITE)
196 else return COLOR_DEFAULT;
200 ui_init_color_pairs_user()
202 init_pair(CP_HEADER, opt_color_to_color(STR_COLOR_HEADER_FG),
203 opt_color_to_color(STR_COLOR_HEADER_BG));
204 init_pair(CP_FOOTER, opt_color_to_color(STR_COLOR_FOOTER_FG),
205 opt_color_to_color(STR_COLOR_FOOTER_BG));
206 init_pair(CP_LIST_EVEN, opt_color_to_color(STR_COLOR_LIST_EVEN_FG),
207 opt_color_to_color(STR_COLOR_LIST_EVEN_BG));
208 init_pair(CP_LIST_ODD, opt_color_to_color(STR_COLOR_LIST_ODD_FG),
209 opt_color_to_color(STR_COLOR_LIST_ODD_BG));
210 init_pair(CP_LIST_HEADER, opt_color_to_color(STR_COLOR_LIST_HEADER_FG),
211 opt_color_to_color(STR_COLOR_LIST_HEADER_BG));
212 init_pair(CP_LIST_HIGHLIGHT, opt_color_to_color(STR_COLOR_LIST_HIGHLIGHT_FG),
213 opt_color_to_color(STR_COLOR_LIST_HIGHLIGHT_BG));
214 init_pair(CP_TAB_BORDER, opt_color_to_color(STR_COLOR_TAB_BORDER_FG),
215 opt_color_to_color(STR_COLOR_TAB_BORDER_BG));
216 init_pair(CP_TAB_LABEL, opt_color_to_color(STR_COLOR_TAB_LABEL_FG),
217 opt_color_to_color(STR_COLOR_TAB_LABEL_BG));
218 init_pair(CP_FIELD_NAME, opt_color_to_color(STR_COLOR_FIELD_NAME_FG),
219 opt_color_to_color(STR_COLOR_FIELD_NAME_BG));
220 init_pair(CP_FIELD_VALUE, opt_color_to_color(STR_COLOR_FIELD_VALUE_FG),
221 opt_color_to_color(STR_COLOR_FIELD_VALUE_BG));
229 fprintf(stderr, "init_abook():\n");
230 fprintf(stderr, " COLS = %d, LINES = %d\n", COLS, LINES);
232 if( LINES < MIN_LINES || COLS < MIN_COLS ) {
233 clear(); refresh(); endwin();
234 fprintf(stderr, _("Your terminal size is %dx%d\n"), COLS, LINES);
235 fprintf(stderr, _("Terminal is too small. Minimum terminal "
237 "%dx%d\n"), MIN_COLS, MIN_LINES);
244 ui_initialized = TRUE;
247 signal(SIGWINCH, win_changed);
262 ui_initialized = FALSE;
267 headerline(const char *str)
271 wattrset(top, COLOR_PAIR(CP_HEADER));
272 mvwhline(top, 0, 0, ' ', COLS);
273 mvwhline(top, 1, 0, UI_HLINE_CHAR, COLS);
275 mvwprintw(top, 0, 0, "%s | %s", PACKAGE " " VERSION, str);
293 refresh_statusline();
294 headerline(gettext(MAIN_HELPLINE));
302 statusline_msg(const char *msg)
307 statusline_addstr(msg);
310 fprintf(stderr, "statusline_msg(\"%s\")\n", msg);
318 statusline_addstr(const char *str)
320 mvwaddstr(bottom, 1, 0, str);
325 /* Same as statusline_addstr(), but hilight "<str>" sequences if the terminal
328 statusline_addhlstr(const char *str)
330 #if defined(A_BOLD) && defined(A_NORMAL) && defined(A_DIM)
331 const char *p = str, *start = str;
336 if(!*p || strchr("<>", *p)) {
338 wattrset(bottom, (*p == '>') ? A_BOLD : A_NORMAL);
339 tmp = xstrndup(start, p - start);
340 mvwaddstr(bottom, 1, pos, tmp);
341 pos += strwidth(tmp);
347 /* show tag markers */
348 wattrset(bottom, A_DIM);
349 mvwaddch(bottom, 1, pos++, *p);
354 wattrset(bottom, A_NORMAL);
361 mvwaddstr(bottom, 1, 0, str);
369 statusline_askchoice(const char *msg, const char *choices, short dflt)
374 assert((dflt >= 0) && (dflt <= strlen(choices)));
377 s = strdup_printf("%s [%c]", msg, choices[dflt - 1]);
378 statusline_addhlstr(s);
381 statusline_addhlstr(msg);
385 ch = tolower(getch());
387 if(ch == 7) /* ctrl+G */
390 if(dflt && (ch == '\r')) /* default choice */
393 if((s = strchr(choices, ch)))
394 return (s - choices + 1);
399 ui_readline(const char *prompt, char *s, size_t limit, bool use_completion)
404 mvwaddstr(bottom, 1, 0, prompt);
408 ret = abook_readline(bottom, y, x, s, use_completion);
412 if(strlen(ret) > limit && limit > 0)
420 statusline_ask_boolean(const char *msg, int def)
423 char *msg2 = strconcat(msg, def ? _(" (Y/n)?") : _(" (y/N)?"), NULL);
426 statusline_addstr(msg2);
430 ch = tolower(getch());
432 if(ch == *(S_("keybinding for no|n")))
434 else if(ch == *(S_("keybinding for yes|y")))
449 wattrset(bottom, COLOR_PAIR(CP_FOOTER));
450 mvwhline(bottom, 0, 0, UI_HLINE_CHAR, COLS);
457 ask_filename(const char *prompt)
463 buf = ui_readline(prompt, NULL, -1, 1);
484 display_help(int help)
500 helpw = newwin(LINES - 5, COLS - 6, 2, 3);
502 headerline(_("help"));
504 for(i = 0; tbl[i] != NULL; i++) {
505 waddstr(helpw, gettext(tbl[i]));
506 if( (!((i + 1) % (LINES - 8))) ||
507 (tbl[i + 1] == NULL) ) {
510 refresh_statusline();
511 if(statusline_msg(_("Press any key to continue..."))
526 extern char *selected;
534 can_resize = TRUE; /* it's safe to resize now */
535 if(!opt_get_bool(BOOL_SHOW_CURSOR))
540 if(!opt_get_bool(BOOL_SHOW_CURSOR))
542 can_resize = FALSE; /* it's not safe to resize anymore */
543 if(ch == KEY_MOUSE) {
545 bool double_clicked = was_double_click();
546 if(getmouse(&event) == OK) {
547 if(event.bstate & BUTTON1_CLICKED
548 || event.bstate & BUTTON1_DOUBLE_CLICKED) {
552 list_set_curitem(event.y + list_get_firstitem() - LIST_TOP);
558 } else if(event.bstate & BUTTON4_PRESSED) {
560 } else if(event.bstate & BUTTON5_PRESSED) {
567 case 'Q': quit_abook(QUIT_DONTSAVE); break;
568 case 'P': print_stderr(selected_items() ?
569 -1 : list_get_curitem());
572 display_help(HELP_MAIN);
575 case 'a': add_item(); break;
576 case '\r': edit_item(-1); break;
579 case 'r': ui_remove_items(); break;
580 case 'D': duplicate_item(); break;
581 case 12: refresh_screen(); break;
584 case KEY_UP: scroll_up(); break;
586 case KEY_DOWN: scroll_down(); break;
588 case KEY_PPAGE: page_up(); break;
590 case KEY_NPAGE: page_down(); break;
593 case KEY_HOME: goto_home(); break;
595 case KEY_END: goto_end(); break;
597 case 'w': save_database();
599 case 'l': ui_read_database(); break;
600 case 'i': import_database(); break;
601 case 'e': export_database(); break;
602 case 'C': ui_clear_database(); break;
604 case 'o': ui_open_datafile(); break;
606 case 's': sort_by_field("name");break;
607 case 'S': sort_surname(); break;
608 case 'F': sort_by_field(NULL); break;
610 case '/': ui_find(0); break;
612 case '\\': ui_find(1); break;
614 case ' ': if(list_get_curitem() >= 0) {
615 list_invert_curitem_selection();
616 ui_print_number_of_items();
620 case '+': select_all();
623 case '-': select_none();
626 case '*': invert_selection();
629 case 'A': move_curitem(MOVE_ITEM_UP);
631 case 'Z': move_curitem(MOVE_ITEM_DOWN);
634 case 'm': launch_mutt(selected_items() ?
635 -1 : list_get_curitem());
639 case 'p': ui_print_database(); break;
641 case 'v': launch_wwwbrowser(list_get_curitem());
654 if(statusline_ask_boolean(_("Remove selected item(s)"), TRUE))
655 remove_selected_items();
664 if(statusline_ask_boolean(_("Clear WHOLE database"), FALSE)) {
674 static char findstr[MAX_FIELD_LEN];
675 int search_fields[] = {NAME, EMAIL, NICK, -1};
684 s = ui_readline("/", findstr, MAX_FIELD_LEN - 1, 0);
687 return; /* user cancelled (ctrl-G) */
689 strncpy(findstr, s, MAX_FIELD_LEN);
694 if( (item = find_item(findstr, list_get_curitem() + !!next,
695 search_fields)) < 0 &&
696 (item = find_item(findstr, 0, search_fields)) >= 0)
697 statusline_addstr(_("Search hit bottom, continuing at top"));
700 list_set_curitem(item);
706 ui_print_number_of_items()
708 char *str = strdup_printf(" " "|%3d/%3d",
709 selected_items(), db_n_items());
711 attrset(COLOR_PAIR(CP_HEADER));
712 mvaddstr(0, COLS-strlen(str), str);
722 if(!list_is_empty()) {
723 msg = strdup_printf(_("Your current data will be lost - "
724 "Press '%c' to continue"),
725 *(S_("keybinding for yes|y")));
726 if(!statusline_ask_boolean(msg, FALSE)) {
733 load_database(datafile);
742 char *command = opt_get_str(STR_PRINT_COMMAND);
748 switch(statusline_askchoice(_("Print <a>ll, print <s>elected, or <c>ancel?"), S_("keybindings:all/selected/cancel|asc"), 3)) {
753 if( !selected_items() ) {
754 statusline_msg(_("No selected items"));
757 mode = ENUM_SELECTED;
766 if( ! *command || (handle = popen(command, "w")) == NULL)
769 fexport("text", handle, mode);
780 filename = ask_filename(_("File to open: "));
782 if(!filename || ! *filename) {
788 if(opt_get_bool(BOOL_AUTOSAVE))
790 else if(statusline_ask_boolean(_("Save current database"), FALSE))
795 load_database(filename);
797 if(list_is_empty()) {
798 statusline_msg(_("Sorry, the specified file appears not to be a valid abook addressbook"));
799 load_database(datafile);
802 datafile = xstrdup(filename);
808 alternative_datafile = TRUE;