• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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