2.0 init
[external/alsa-utils.git] / alsamixer / textbox.c
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 }