/* * textbox.c - show a text box for messages, files or help * Copyright (c) 1998,1999 Tim Janik * Jaroslav Kysela * Copyright (c) 2009 Clemens Ladisch * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "aconfig.h" #include #include #include #include #include CURSESINC #include #include "gettext_curses.h" #include "utils.h" #include "die.h" #include "mem.h" #include "colors.h" #include "widget.h" #include "textbox.h" #define MAX_FILE_SIZE 1048576 static void create_text_box(const char *const *lines, unsigned int count, const char *title, int attrs); void show_error(const char *msg, int err) { const char *lines[2]; unsigned int count; lines[0] = msg; count = 1; if (err) { lines[1] = strerror(err); count = 2; } create_text_box(lines, count, _("Error"), attr_errormsg); } void show_alsa_error(const char *msg, int err) { const char *lines[2]; unsigned int count; lines[0] = msg; count = 1; if (err < 0) { lines[1] = snd_strerror(err); count = 2; } create_text_box(lines, count, _("Error"), attr_errormsg); } static char *read_file(const char *file_name, unsigned int *file_size) { FILE *f; int err; char *buf; unsigned int allocated = 2048; unsigned int bytes_read; f = fopen(file_name, "r"); if (!f) { err = errno; buf = casprintf(_("Cannot open file \"%s\"."), file_name); show_error(buf, err); free(buf); return NULL; } *file_size = 0; buf = NULL; do { allocated *= 2; buf = crealloc(buf, allocated); bytes_read = fread(buf + *file_size, 1, allocated - *file_size, f); *file_size += bytes_read; } while (*file_size == allocated && allocated < MAX_FILE_SIZE); fclose(f); if (*file_size > 0 && buf[*file_size - 1] != '\n' && *file_size < allocated) { buf[*file_size] = '\n'; ++*file_size; } return buf; } void show_textfile(const char *file_name) { char *buf; unsigned int file_size; unsigned int line_count; unsigned int i; const char **lines; const char *start_line; buf = read_file(file_name, &file_size); if (!buf) return; line_count = 0; for (i = 0; i < file_size; ++i) line_count += buf[i] == '\n'; lines = ccalloc(line_count, sizeof *lines); line_count = 0; start_line = buf; for (i = 0; i < file_size; ++i) { if (buf[i] == '\n') { lines[line_count++] = start_line; buf[i] = '\0'; start_line = &buf[i + 1]; } if (buf[i] == '\t') buf[i] = ' '; } create_text_box(lines, line_count, file_name, attr_textbox); free(lines); free(buf); } void show_text(const char *const *lines, unsigned int count, const char *title) { create_text_box(lines, count, title, attr_textbox); } /**********************************************************************/ static struct widget text_widget; static char *title; static int widget_attrs; static char **text_lines; static unsigned int text_lines_count; static int max_line_width; static int text_box_y; static int text_box_x; static int max_scroll_y; static int max_scroll_x; static int current_top; static int current_left; static void update_text_lines(void) { int i; int width; const char *line_begin; const char *line_end; int cur_y, cur_x; int rest_of_line; for (i = 0; i < text_box_y; ++i) { width = current_left; line_begin = mbs_at_width(text_lines[current_top + i], &width, 1); wmove(text_widget.window, i + 1, 1); if (width > current_left) waddch(text_widget.window, ' '); if (*line_begin != '\0') { width = text_box_x - (width > current_left); line_end = mbs_at_width(line_begin, &width, -1); if (width) waddnstr(text_widget.window, line_begin, line_end - line_begin); } getyx(text_widget.window, cur_y, cur_x); if (cur_y == i + 1) { rest_of_line = text_box_x + 1 - cur_x; if (rest_of_line > 0) wprintw(text_widget.window, "%*s", rest_of_line, ""); } } } static void update_y_scroll_bar(void) { int length; int begin, end; int i; if (max_scroll_y <= 0 || text_lines_count == 0) return; length = text_box_y * text_box_y / text_lines_count; if (length >= text_box_y) return; begin = current_top * (text_box_y - length) / max_scroll_y; end = begin + length; for (i = 0; i < text_box_y; ++i) mvwaddch(text_widget.window, i + 1, text_box_x + 1, i >= begin && i < end ? ACS_BOARD : ' '); } static void update_x_scroll_bar(void) { int length; int begin, end; int i; if (max_scroll_x <= 0 || max_line_width <= 0) return; length = text_box_x * text_box_x / max_line_width; if (length >= text_box_x) return; begin = current_left * (text_box_x - length) / max_scroll_x; end = begin + length; wmove(text_widget.window, text_box_y + 1, 1); for (i = 0; i < text_box_x; ++i) waddch(text_widget.window, i >= begin && i < end ? ACS_BOARD : ' '); } static void move_x(int delta) { int left; left = current_left + delta; if (left < 0) left = 0; else if (left > max_scroll_x) left = max_scroll_x; if (left != current_left) { current_left = left; update_text_lines(); update_x_scroll_bar(); } } static void move_y(int delta) { int top; top = current_top + delta; if (top < 0) top = 0; else if (top > max_scroll_y) top = max_scroll_y; if (top != current_top) { current_top = top; update_text_lines(); update_y_scroll_bar(); } } static void on_handle_key(int key) { switch (key) { case 10: case 13: case 27: case KEY_CANCEL: case KEY_ENTER: case KEY_CLOSE: case KEY_EXIT: text_widget.close(); break; case KEY_DOWN: case KEY_SF: case 'J': case 'j': case 'X': case 'x': move_y(1); break; case KEY_UP: case KEY_SR: case 'K': case 'k': case 'W': case 'w': move_y(-1); break; case KEY_LEFT: case 'H': case 'h': case 'P': case 'p': move_x(-1); break; case KEY_RIGHT: case 'L': case 'l': case 'N': case 'n': move_x(1); break; case KEY_NPAGE: case ' ': move_y(text_box_y); break; case KEY_PPAGE: case KEY_BACKSPACE: case 'B': case 'b': move_y(-text_box_y); break; case KEY_HOME: case KEY_BEG: move_x(-max_scroll_x); break; case KEY_LL: case KEY_END: move_x(max_scroll_x); break; case '\t': move_x(8); break; case KEY_BTAB: move_x(-8); break; } } static bool create(void) { int len, width; if (screen_lines < 3 || screen_cols < 8) { text_widget.close(); beep(); return FALSE; } width = max_line_width; len = get_mbs_width(title) + 2; if (width < len) width = len; text_box_y = text_lines_count; if (text_box_y > screen_lines - 2) text_box_y = screen_lines - 2; max_scroll_y = text_lines_count - text_box_y; text_box_x = width; if (text_box_x > screen_cols - 2) text_box_x = screen_cols - 2; max_scroll_x = max_line_width - text_box_x; widget_init(&text_widget, text_box_y + 2, text_box_x + 2, SCREEN_CENTER, SCREEN_CENTER, widget_attrs, WIDGET_BORDER); mvwprintw(text_widget.window, 0, (text_box_x + 2 - get_mbs_width(title) - 2) / 2, " %s ", title); if (current_top > max_scroll_y) current_top = max_scroll_y; if (current_left > max_scroll_x) current_left = max_scroll_x; update_text_lines(); update_y_scroll_bar(); update_x_scroll_bar(); return TRUE; } static void on_window_size_changed(void) { create(); } static void on_close(void) { unsigned int i; for (i = 0; i < text_lines_count; ++i) free(text_lines[i]); free(text_lines); widget_free(&text_widget); } static struct widget text_widget = { .handle_key = on_handle_key, .window_size_changed = on_window_size_changed, .close = on_close, }; static void create_text_box(const char *const *lines, unsigned int count, const char *title_, int attrs) { unsigned int i; text_lines = ccalloc(count, sizeof *text_lines); for (i = 0; i < count; ++i) text_lines[i] = cstrdup(lines[i]); text_lines_count = count; max_line_width = get_max_mbs_width(lines, count); title = cstrdup(title_); widget_attrs = attrs; current_top = 0; current_left = 0; create(); }