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