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
37 static void create_text_box(const char *const *lines, unsigned int count,
38 const char *title, int attrs);
39
show_error(const char * msg,int err)40 void 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
show_alsa_error(const char * msg,int err)54 void 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
show_textfile(const char * file_name)68 void 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
show_text(const char * const * lines,unsigned int count,const char * title)104 void 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
111 static struct widget text_widget;
112 static char *title;
113 static int widget_attrs;
114 static char **text_lines;
115 static unsigned int text_lines_count;
116 static int max_line_width;
117 static int text_box_y;
118 static int text_box_x;
119 static int max_scroll_y;
120 static int max_scroll_x;
121 static int current_top;
122 static int current_left;
123
update_text_lines(void)124 static 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
update_y_scroll_bar(void)155 static 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
update_x_scroll_bar(void)170 static 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
move_x(int delta)185 static 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
move_y(int delta)201 static 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
on_handle_key(int key)217 static void on_handle_key(int key)
218 {
219 if (key >= 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
create(void)259 static 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
on_window_size_changed(void)297 static void on_window_size_changed(void)
298 {
299 create();
300 }
301
on_close(void)302 static 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
312 static struct widget text_widget = {
313 .handle_key = on_handle_key,
314 .window_size_changed = on_window_size_changed,
315 .close = on_close,
316 };
317
create_text_box(const char * const * lines,unsigned int count,const char * title_,int attrs)318 static 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