textbox.c (8600B)
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 36 #define MAX_FILE_SIZE 1048576 37 38 static void create_text_box(const char *const *lines, unsigned int count, 39 const char *title, int attrs); 40 41 void show_error(const char *msg, int err) 42 { 43 const char *lines[2]; 44 unsigned int count; 45 46 lines[0] = msg; 47 count = 1; 48 if (err) { 49 lines[1] = strerror(err); 50 count = 2; 51 } 52 create_text_box(lines, count, _("Error"), attr_errormsg); 53 } 54 55 void show_alsa_error(const char *msg, int err) 56 { 57 const char *lines[2]; 58 unsigned int count; 59 60 lines[0] = msg; 61 count = 1; 62 if (err < 0) { 63 lines[1] = snd_strerror(err); 64 count = 2; 65 } 66 create_text_box(lines, count, _("Error"), attr_errormsg); 67 } 68 69 static char *read_file(const char *file_name, unsigned int *file_size) 70 { 71 FILE *f; 72 int err; 73 char *buf; 74 unsigned int allocated = 2048; 75 unsigned int bytes_read; 76 77 f = fopen(file_name, "r"); 78 if (!f) { 79 err = errno; 80 buf = casprintf(_("Cannot open file \"%s\"."), file_name); 81 show_error(buf, err); 82 free(buf); 83 return NULL; 84 } 85 *file_size = 0; 86 buf = NULL; 87 do { 88 allocated *= 2; 89 buf = crealloc(buf, allocated); 90 bytes_read = fread(buf + *file_size, 1, allocated - *file_size, f); 91 *file_size += bytes_read; 92 } while (*file_size == allocated && allocated < MAX_FILE_SIZE); 93 fclose(f); 94 if (*file_size > 0 && buf[*file_size - 1] != '\n' && *file_size < allocated) { 95 buf[*file_size] = '\n'; 96 ++*file_size; 97 } 98 return buf; 99 } 100 101 void show_textfile(const char *file_name) 102 { 103 char *buf; 104 unsigned int file_size; 105 unsigned int line_count; 106 unsigned int i; 107 const char **lines; 108 const char *start_line; 109 110 buf = read_file(file_name, &file_size); 111 if (!buf) 112 return; 113 line_count = 0; 114 for (i = 0; i < file_size; ++i) 115 line_count += buf[i] == '\n'; 116 lines = ccalloc(line_count, sizeof *lines); 117 line_count = 0; 118 start_line = buf; 119 for (i = 0; i < file_size; ++i) { 120 if (buf[i] == '\n') { 121 lines[line_count++] = start_line; 122 buf[i] = '\0'; 123 start_line = &buf[i + 1]; 124 } 125 if (buf[i] == '\t') 126 buf[i] = ' '; 127 } 128 create_text_box(lines, line_count, file_name, attr_textbox); 129 free(lines); 130 free(buf); 131 } 132 133 void show_text(const char *const *lines, unsigned int count, const char *title) 134 { 135 create_text_box(lines, count, title, attr_textbox); 136 } 137 138 /**********************************************************************/ 139 140 static struct widget text_widget; 141 static char *title; 142 static int widget_attrs; 143 static char **text_lines; 144 static unsigned int text_lines_count; 145 static int max_line_width; 146 static int text_box_y; 147 static int text_box_x; 148 static int max_scroll_y; 149 static int max_scroll_x; 150 static int current_top; 151 static int current_left; 152 153 static void update_text_lines(void) 154 { 155 int i; 156 int width; 157 const char *line_begin; 158 const char *line_end; 159 int cur_y, cur_x; 160 int rest_of_line; 161 162 for (i = 0; i < text_box_y; ++i) { 163 width = current_left; 164 line_begin = mbs_at_width(text_lines[current_top + i], &width, 1); 165 wmove(text_widget.window, i + 1, 1); 166 if (width > current_left) 167 waddch(text_widget.window, ' '); 168 if (*line_begin != '\0') { 169 width = text_box_x - (width > current_left); 170 line_end = mbs_at_width(line_begin, &width, -1); 171 if (width) 172 waddnstr(text_widget.window, line_begin, 173 line_end - line_begin); 174 } 175 getyx(text_widget.window, cur_y, cur_x); 176 if (cur_y == i + 1) { 177 rest_of_line = text_box_x + 1 - cur_x; 178 if (rest_of_line > 0) 179 wprintw(text_widget.window, "%*s", rest_of_line, ""); 180 } 181 } 182 } 183 184 static void update_y_scroll_bar(void) 185 { 186 int length; 187 int begin, end; 188 int i; 189 190 if (max_scroll_y <= 0 || text_lines_count == 0) 191 return; 192 length = text_box_y * text_box_y / text_lines_count; 193 if (length >= text_box_y) 194 return; 195 begin = current_top * (text_box_y - length) / max_scroll_y; 196 end = begin + length; 197 for (i = 0; i < text_box_y; ++i) 198 mvwaddch(text_widget.window, i + 1, text_box_x + 1, 199 i >= begin && i < end ? ACS_BOARD : ' '); 200 } 201 202 static void update_x_scroll_bar(void) 203 { 204 int length; 205 int begin, end; 206 int i; 207 208 if (max_scroll_x <= 0 || max_line_width <= 0) 209 return; 210 length = text_box_x * text_box_x / max_line_width; 211 if (length >= text_box_x) 212 return; 213 begin = current_left * (text_box_x - length) / max_scroll_x; 214 end = begin + length; 215 wmove(text_widget.window, text_box_y + 1, 1); 216 for (i = 0; i < text_box_x; ++i) 217 waddch(text_widget.window, i >= begin && i < end ? ACS_BOARD : ' '); 218 } 219 220 static void move_x(int delta) 221 { 222 int left; 223 224 left = current_left + delta; 225 if (left < 0) 226 left = 0; 227 else if (left > max_scroll_x) 228 left = max_scroll_x; 229 if (left != current_left) { 230 current_left = left; 231 update_text_lines(); 232 update_x_scroll_bar(); 233 } 234 } 235 236 static void move_y(int delta) 237 { 238 int top; 239 240 top = current_top + delta; 241 if (top < 0) 242 top = 0; 243 else if (top > max_scroll_y) 244 top = max_scroll_y; 245 if (top != current_top) { 246 current_top = top; 247 update_text_lines(); 248 update_y_scroll_bar(); 249 } 250 } 251 252 static void on_handle_key(int key) 253 { 254 switch (key) { 255 case 10: 256 case 13: 257 case 27: 258 case KEY_CANCEL: 259 case KEY_ENTER: 260 case KEY_CLOSE: 261 case KEY_EXIT: 262 text_widget.close(); 263 break; 264 case KEY_DOWN: 265 case KEY_SF: 266 case 'J': 267 case 'j': 268 case 'X': 269 case 'x': 270 move_y(1); 271 break; 272 case KEY_UP: 273 case KEY_SR: 274 case 'K': 275 case 'k': 276 case 'W': 277 case 'w': 278 move_y(-1); 279 break; 280 case KEY_LEFT: 281 case 'H': 282 case 'h': 283 case 'P': 284 case 'p': 285 move_x(-1); 286 break; 287 case KEY_RIGHT: 288 case 'L': 289 case 'l': 290 case 'N': 291 case 'n': 292 move_x(1); 293 break; 294 case KEY_NPAGE: 295 case ' ': 296 move_y(text_box_y); 297 break; 298 case KEY_PPAGE: 299 case KEY_BACKSPACE: 300 case 'B': 301 case 'b': 302 move_y(-text_box_y); 303 break; 304 case KEY_HOME: 305 case KEY_BEG: 306 move_x(-max_scroll_x); 307 break; 308 case KEY_LL: 309 case KEY_END: 310 move_x(max_scroll_x); 311 break; 312 case '\t': 313 move_x(8); 314 break; 315 case KEY_BTAB: 316 move_x(-8); 317 break; 318 } 319 } 320 321 static bool create(void) 322 { 323 int len, width; 324 325 if (screen_lines < 3 || screen_cols < 8) { 326 text_widget.close(); 327 beep(); 328 return FALSE; 329 } 330 331 width = max_line_width; 332 len = get_mbs_width(title) + 2; 333 if (width < len) 334 width = len; 335 336 text_box_y = text_lines_count; 337 if (text_box_y > screen_lines - 2) 338 text_box_y = screen_lines - 2; 339 max_scroll_y = text_lines_count - text_box_y; 340 text_box_x = width; 341 if (text_box_x > screen_cols - 2) 342 text_box_x = screen_cols - 2; 343 max_scroll_x = max_line_width - text_box_x; 344 345 widget_init(&text_widget, text_box_y + 2, text_box_x + 2, 346 SCREEN_CENTER, SCREEN_CENTER, widget_attrs, WIDGET_BORDER); 347 mvwprintw(text_widget.window, 0, (text_box_x + 2 - get_mbs_width(title) - 2) / 2, " %s ", title); 348 349 if (current_top > max_scroll_y) 350 current_top = max_scroll_y; 351 if (current_left > max_scroll_x) 352 current_left = max_scroll_x; 353 update_text_lines(); 354 update_y_scroll_bar(); 355 update_x_scroll_bar(); 356 return TRUE; 357 } 358 359 static void on_window_size_changed(void) 360 { 361 create(); 362 } 363 364 static void on_close(void) 365 { 366 unsigned int i; 367 368 for (i = 0; i < text_lines_count; ++i) 369 free(text_lines[i]); 370 free(text_lines); 371 widget_free(&text_widget); 372 } 373 374 static struct widget text_widget = { 375 .handle_key = on_handle_key, 376 .window_size_changed = on_window_size_changed, 377 .close = on_close, 378 }; 379 380 static void create_text_box(const char *const *lines, unsigned int count, 381 const char *title_, int attrs) 382 { 383 unsigned int i; 384 385 text_lines = ccalloc(count, sizeof *text_lines); 386 for (i = 0; i < count; ++i) 387 text_lines[i] = cstrdup(lines[i]); 388 text_lines_count = count; 389 max_line_width = get_max_mbs_width(lines, count); 390 title = cstrdup(title_); 391 widget_attrs = attrs; 392 393 current_top = 0; 394 current_left = 0; 395 396 create(); 397 }