1/* 2 * textbox.c - show a text box for messages, files or help 3 * Copyright (c) 1998,1999 Tim Janik 4 * Jaroslav Kysela <perex@perex.cz> 5 * Copyright (c) 2009 Clemens Ladisch <clemens@ladisch.de> 6 * 7 * This program is free software: you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation, either version 2 of the License, or 10 * (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program. If not, see <http://www.gnu.org/licenses/>. 19 */ 20 21#include "aconfig.h" 22#include <stdio.h> 23#include <stdlib.h> 24#include <string.h> 25#include <errno.h> 26#include CURSESINC 27#include <alsa/asoundlib.h> 28#include "gettext_curses.h" 29#include "utils.h" 30#include "die.h" 31#include "mem.h" 32#include "colors.h" 33#include "widget.h" 34#include "textbox.h" 35#include "bindings.h" 36 37static void create_text_box(const char *const *lines, unsigned int count, 38 const char *title, int attrs); 39 40void show_error(const char *msg, int err) 41{ 42 const char *lines[2]; 43 unsigned int count; 44 45 lines[0] = msg; 46 count = 1; 47 if (err) { 48 lines[1] = strerror(err); 49 count = 2; 50 } 51 create_text_box(lines, count, _("Error"), attrs.errormsg); 52} 53 54void show_alsa_error(const char *msg, int err) 55{ 56 const char *lines[2]; 57 unsigned int count; 58 59 lines[0] = msg; 60 count = 1; 61 if (err < 0) { 62 lines[1] = snd_strerror(err); 63 count = 2; 64 } 65 create_text_box(lines, count, _("Error"), attrs.errormsg); 66} 67 68void show_textfile(const char *file_name) 69{ 70 char *buf; 71 unsigned int file_size; 72 unsigned int line_count; 73 unsigned int i; 74 const char **lines; 75 const char *start_line; 76 77 buf = read_file(file_name, &file_size); 78 if (!buf) { 79 int err = errno; 80 buf = casprintf(_("Cannot open file \"%s\"."), file_name); 81 show_error(buf, err); 82 return; 83 } 84 line_count = 0; 85 for (i = 0; i < file_size; ++i) 86 line_count += buf[i] == '\n'; 87 lines = ccalloc(line_count, sizeof *lines); 88 line_count = 0; 89 start_line = buf; 90 for (i = 0; i < file_size; ++i) { 91 if (buf[i] == '\n') { 92 lines[line_count++] = start_line; 93 buf[i] = '\0'; 94 start_line = &buf[i + 1]; 95 } 96 if (buf[i] == '\t') 97 buf[i] = ' '; 98 } 99 create_text_box(lines, line_count, file_name, attrs.textbox); 100 free(lines); 101 free(buf); 102} 103 104void show_text(const char *const *lines, unsigned int count, const char *title) 105{ 106 create_text_box(lines, count, title, attrs.textbox); 107} 108 109/**********************************************************************/ 110 111static struct widget text_widget; 112static char *title; 113static int widget_attrs; 114static char **text_lines; 115static unsigned int text_lines_count; 116static int max_line_width; 117static int text_box_y; 118static int text_box_x; 119static int max_scroll_y; 120static int max_scroll_x; 121static int current_top; 122static int current_left; 123 124static void update_text_lines(void) 125{ 126 int i; 127 int width; 128 const char *line_begin; 129 const char *line_end; 130 int cur_y, cur_x; 131 int rest_of_line; 132 133 for (i = 0; i < text_box_y; ++i) { 134 width = current_left; 135 line_begin = mbs_at_width(text_lines[current_top + i], &width, 1); 136 wmove(text_widget.window, i + 1, 1); 137 if (width > current_left) 138 waddch(text_widget.window, ' '); 139 if (*line_begin != '\0') { 140 width = text_box_x - (width > current_left); 141 line_end = mbs_at_width(line_begin, &width, -1); 142 if (width) 143 waddnstr(text_widget.window, line_begin, 144 line_end - line_begin); 145 } 146 getyx(text_widget.window, cur_y, cur_x); 147 if (cur_y == i + 1) { 148 rest_of_line = text_box_x + 1 - cur_x; 149 if (rest_of_line > 0) 150 wprintw(text_widget.window, "%*s", rest_of_line, ""); 151 } 152 } 153} 154 155static void update_y_scroll_bar(void) 156{ 157 int length; 158 int begin; 159 160 if (max_scroll_y <= 0 || text_lines_count == 0) 161 return; 162 length = text_box_y * text_box_y / text_lines_count; 163 if (length >= text_box_y) 164 return; 165 begin = current_top * (text_box_y - length) / max_scroll_y; 166 mvwvline(text_widget.window, 1, text_box_x + 1, ' ', text_box_y); 167 mvwvline(text_widget.window, begin + 1, text_box_x + 1, ACS_BOARD, length); 168} 169 170static void update_x_scroll_bar(void) 171{ 172 int length; 173 int begin; 174 175 if (max_scroll_x <= 0 || max_line_width <= 0) 176 return; 177 length = text_box_x * text_box_x / max_line_width; 178 if (length >= text_box_x) 179 return; 180 begin = current_left * (text_box_x - length) / max_scroll_x; 181 mvwhline(text_widget.window, text_box_y + 1, 1, ' ', text_box_x); 182 mvwhline(text_widget.window, text_box_y + 1, begin + 1, ACS_BOARD, length); 183} 184 185static void move_x(int delta) 186{ 187 int left; 188 189 left = current_left + delta; 190 if (left < 0) 191 left = 0; 192 else if (left > max_scroll_x) 193 left = max_scroll_x; 194 if (left != current_left) { 195 current_left = left; 196 update_text_lines(); 197 update_x_scroll_bar(); 198 } 199} 200 201static void move_y(int delta) 202{ 203 int top; 204 205 top = current_top + delta; 206 if (top < 0) 207 top = 0; 208 else if (top > max_scroll_y) 209 top = max_scroll_y; 210 if (top != current_top) { 211 current_top = top; 212 update_text_lines(); 213 update_y_scroll_bar(); 214 } 215} 216 217static void on_handle_key(int key) 218{ 219 if (key >= (int)ARRAY_SIZE(textbox_bindings)) 220 return; 221 222 switch (textbox_bindings[key]) { 223 case CMD_TEXTBOX_CLOSE: 224 text_widget.close(); 225 break; 226 case CMD_TEXTBOX_DOWN: 227 move_y(1); 228 break; 229 case CMD_TEXTBOX_UP: 230 move_y(-1); 231 break; 232 case CMD_TEXTBOX_LEFT: 233 move_x(-1); 234 break; 235 case CMD_TEXTBOX_RIGHT: 236 move_x(1); 237 break; 238 case CMD_TEXTBOX_PAGE_DOWN: 239 move_y(text_box_y); 240 break; 241 case CMD_TEXTBOX_PAGE_UP: 242 move_y(-text_box_y); 243 break; 244 case CMD_TEXTBOX_TOP: 245 move_x(-max_scroll_x); 246 break; 247 case CMD_TEXTBOX_BOTTOM: 248 move_x(max_scroll_x); 249 break; 250 case CMD_TEXTBOX_PAGE_RIGHT: 251 move_x(8); 252 break; 253 case CMD_TEXTBOX_PAGE_LEFT: 254 move_x(-8); 255 break; 256 } 257} 258 259static bool create(void) 260{ 261 int len, width; 262 263 if (screen_lines < 3 || screen_cols < 8) { 264 text_widget.close(); 265 beep(); 266 return FALSE; 267 } 268 269 width = max_line_width; 270 len = get_mbs_width(title) + 2; 271 if (width < len) 272 width = len; 273 274 text_box_y = text_lines_count; 275 if (text_box_y > screen_lines - 2) 276 text_box_y = screen_lines - 2; 277 max_scroll_y = text_lines_count - text_box_y; 278 text_box_x = width; 279 if (text_box_x > screen_cols - 2) 280 text_box_x = screen_cols - 2; 281 max_scroll_x = max_line_width - text_box_x; 282 283 widget_init(&text_widget, text_box_y + 2, text_box_x + 2, 284 SCREEN_CENTER, SCREEN_CENTER, widget_attrs, WIDGET_BORDER); 285 mvwprintw(text_widget.window, 0, (text_box_x + 2 - get_mbs_width(title) - 2) / 2, " %s ", title); 286 287 if (current_top > max_scroll_y) 288 current_top = max_scroll_y; 289 if (current_left > max_scroll_x) 290 current_left = max_scroll_x; 291 update_text_lines(); 292 update_y_scroll_bar(); 293 update_x_scroll_bar(); 294 return TRUE; 295} 296 297static void on_window_size_changed(void) 298{ 299 create(); 300} 301 302static void on_close(void) 303{ 304 unsigned int i; 305 306 for (i = 0; i < text_lines_count; ++i) 307 free(text_lines[i]); 308 free(text_lines); 309 widget_free(&text_widget); 310} 311 312static struct widget text_widget = { 313 .handle_key = on_handle_key, 314 .window_size_changed = on_window_size_changed, 315 .close = on_close, 316}; 317 318static void create_text_box(const char *const *lines, unsigned int count, 319 const char *title_, int attrs) 320{ 321 unsigned int i; 322 323 text_lines = ccalloc(count, sizeof *text_lines); 324 for (i = 0; i < count; ++i) 325 text_lines[i] = cstrdup(lines[i]); 326 text_lines_count = count; 327 max_line_width = get_max_mbs_width(lines, count); 328 title = cstrdup(title_); 329 widget_attrs = attrs; 330 331 current_top = 0; 332 current_left = 0; 333 334 create(); 335} 336