aboutsummaryrefslogtreecommitdiff
path: root/alsamixer/textbox.c
diff options
context:
space:
mode:
Diffstat (limited to 'alsamixer/textbox.c')
-rw-r--r--alsamixer/textbox.c397
1 files changed, 397 insertions, 0 deletions
diff --git a/alsamixer/textbox.c b/alsamixer/textbox.c
new file mode 100644
index 0000000..d743a14
--- /dev/null
+++ b/alsamixer/textbox.c
@@ -0,0 +1,397 @@
+/*
+ * textbox.c - show a text box for messages, files or help
+ * Copyright (c) 1998,1999 Tim Janik <timj@gtk.org>
+ * Jaroslav Kysela <perex@perex.cz>
+ * Copyright (c) 2009 Clemens Ladisch <clemens@ladisch.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "aconfig.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include CURSESINC
+#include <alsa/asoundlib.h>
+#include "gettext_curses.h"
+#include "utils.h"
+#include "die.h"
+#include "mem.h"
+#include "colors.h"
+#include "widget.h"
+#include "textbox.h"
+
+#define MAX_FILE_SIZE 1048576
+
+static void create_text_box(const char *const *lines, unsigned int count,
+ const char *title, int attrs);
+
+void show_error(const char *msg, int err)
+{
+ const char *lines[2];
+ unsigned int count;
+
+ lines[0] = msg;
+ count = 1;
+ if (err) {
+ lines[1] = strerror(err);
+ count = 2;
+ }
+ create_text_box(lines, count, _("Error"), attr_errormsg);
+}
+
+void show_alsa_error(const char *msg, int err)
+{
+ const char *lines[2];
+ unsigned int count;
+
+ lines[0] = msg;
+ count = 1;
+ if (err < 0) {
+ lines[1] = snd_strerror(err);
+ count = 2;
+ }
+ create_text_box(lines, count, _("Error"), attr_errormsg);
+}
+
+static char *read_file(const char *file_name, unsigned int *file_size)
+{
+ FILE *f;
+ int err;
+ char *buf;
+ unsigned int allocated = 2048;
+ unsigned int bytes_read;
+
+ f = fopen(file_name, "r");
+ if (!f) {
+ err = errno;
+ buf = casprintf(_("Cannot open file \"%s\"."), file_name);
+ show_error(buf, err);
+ free(buf);
+ return NULL;
+ }
+ *file_size = 0;
+ buf = NULL;
+ do {
+ allocated *= 2;
+ buf = crealloc(buf, allocated);
+ bytes_read = fread(buf + *file_size, 1, allocated - *file_size, f);
+ *file_size += bytes_read;
+ } while (*file_size == allocated && allocated < MAX_FILE_SIZE);
+ fclose(f);
+ if (*file_size > 0 && buf[*file_size - 1] != '\n' && *file_size < allocated) {
+ buf[*file_size] = '\n';
+ ++*file_size;
+ }
+ return buf;
+}
+
+void show_textfile(const char *file_name)
+{
+ char *buf;
+ unsigned int file_size;
+ unsigned int line_count;
+ unsigned int i;
+ const char **lines;
+ const char *start_line;
+
+ buf = read_file(file_name, &file_size);
+ if (!buf)
+ return;
+ line_count = 0;
+ for (i = 0; i < file_size; ++i)
+ line_count += buf[i] == '\n';
+ lines = ccalloc(line_count, sizeof *lines);
+ line_count = 0;
+ start_line = buf;
+ for (i = 0; i < file_size; ++i) {
+ if (buf[i] == '\n') {
+ lines[line_count++] = start_line;
+ buf[i] = '\0';
+ start_line = &buf[i + 1];
+ }
+ if (buf[i] == '\t')
+ buf[i] = ' ';
+ }
+ create_text_box(lines, line_count, file_name, attr_textbox);
+ free(lines);
+ free(buf);
+}
+
+void show_text(const char *const *lines, unsigned int count, const char *title)
+{
+ create_text_box(lines, count, title, attr_textbox);
+}
+
+/**********************************************************************/
+
+static struct widget text_widget;
+static char *title;
+static int widget_attrs;
+static char **text_lines;
+static unsigned int text_lines_count;
+static int max_line_width;
+static int text_box_y;
+static int text_box_x;
+static int max_scroll_y;
+static int max_scroll_x;
+static int current_top;
+static int current_left;
+
+static void update_text_lines(void)
+{
+ int i;
+ int width;
+ const char *line_begin;
+ const char *line_end;
+ int cur_y, cur_x;
+ int rest_of_line;
+
+ for (i = 0; i < text_box_y; ++i) {
+ width = current_left;
+ line_begin = mbs_at_width(text_lines[current_top + i], &width, 1);
+ wmove(text_widget.window, i + 1, 1);
+ if (width > current_left)
+ waddch(text_widget.window, ' ');
+ if (*line_begin != '\0') {
+ width = text_box_x - (width > current_left);
+ line_end = mbs_at_width(line_begin, &width, -1);
+ if (width)
+ waddnstr(text_widget.window, line_begin,
+ line_end - line_begin);
+ }
+ getyx(text_widget.window, cur_y, cur_x);
+ if (cur_y == i + 1) {
+ rest_of_line = text_box_x + 1 - cur_x;
+ if (rest_of_line > 0)
+ wprintw(text_widget.window, "%*s", rest_of_line, "");
+ }
+ }
+}
+
+static void update_y_scroll_bar(void)
+{
+ int length;
+ int begin, end;
+ int i;
+
+ if (max_scroll_y <= 0 || text_lines_count == 0)
+ return;
+ length = text_box_y * text_box_y / text_lines_count;
+ if (length >= text_box_y)
+ return;
+ begin = current_top * (text_box_y - length) / max_scroll_y;
+ end = begin + length;
+ for (i = 0; i < text_box_y; ++i)
+ mvwaddch(text_widget.window, i + 1, text_box_x + 1,
+ i >= begin && i < end ? ACS_BOARD : ' ');
+}
+
+static void update_x_scroll_bar(void)
+{
+ int length;
+ int begin, end;
+ int i;
+
+ if (max_scroll_x <= 0 || max_line_width <= 0)
+ return;
+ length = text_box_x * text_box_x / max_line_width;
+ if (length >= text_box_x)
+ return;
+ begin = current_left * (text_box_x - length) / max_scroll_x;
+ end = begin + length;
+ wmove(text_widget.window, text_box_y + 1, 1);
+ for (i = 0; i < text_box_x; ++i)
+ waddch(text_widget.window, i >= begin && i < end ? ACS_BOARD : ' ');
+}
+
+static void move_x(int delta)
+{
+ int left;
+
+ left = current_left + delta;
+ if (left < 0)
+ left = 0;
+ else if (left > max_scroll_x)
+ left = max_scroll_x;
+ if (left != current_left) {
+ current_left = left;
+ update_text_lines();
+ update_x_scroll_bar();
+ }
+}
+
+static void move_y(int delta)
+{
+ int top;
+
+ top = current_top + delta;
+ if (top < 0)
+ top = 0;
+ else if (top > max_scroll_y)
+ top = max_scroll_y;
+ if (top != current_top) {
+ current_top = top;
+ update_text_lines();
+ update_y_scroll_bar();
+ }
+}
+
+static void on_handle_key(int key)
+{
+ switch (key) {
+ case 10:
+ case 13:
+ case 27:
+ case KEY_CANCEL:
+ case KEY_ENTER:
+ case KEY_CLOSE:
+ case KEY_EXIT:
+ text_widget.close();
+ break;
+ case KEY_DOWN:
+ case KEY_SF:
+ case 'J':
+ case 'j':
+ case 'X':
+ case 'x':
+ move_y(1);
+ break;
+ case KEY_UP:
+ case KEY_SR:
+ case 'K':
+ case 'k':
+ case 'W':
+ case 'w':
+ move_y(-1);
+ break;
+ case KEY_LEFT:
+ case 'H':
+ case 'h':
+ case 'P':
+ case 'p':
+ move_x(-1);
+ break;
+ case KEY_RIGHT:
+ case 'L':
+ case 'l':
+ case 'N':
+ case 'n':
+ move_x(1);
+ break;
+ case KEY_NPAGE:
+ case ' ':
+ move_y(text_box_y);
+ break;
+ case KEY_PPAGE:
+ case KEY_BACKSPACE:
+ case 'B':
+ case 'b':
+ move_y(-text_box_y);
+ break;
+ case KEY_HOME:
+ case KEY_BEG:
+ move_x(-max_scroll_x);
+ break;
+ case KEY_LL:
+ case KEY_END:
+ move_x(max_scroll_x);
+ break;
+ case '\t':
+ move_x(8);
+ break;
+ case KEY_BTAB:
+ move_x(-8);
+ break;
+ }
+}
+
+static bool create(void)
+{
+ int len, width;
+
+ if (screen_lines < 3 || screen_cols < 8) {
+ text_widget.close();
+ beep();
+ return FALSE;
+ }
+
+ width = max_line_width;
+ len = get_mbs_width(title) + 2;
+ if (width < len)
+ width = len;
+
+ text_box_y = text_lines_count;
+ if (text_box_y > screen_lines - 2)
+ text_box_y = screen_lines - 2;
+ max_scroll_y = text_lines_count - text_box_y;
+ text_box_x = width;
+ if (text_box_x > screen_cols - 2)
+ text_box_x = screen_cols - 2;
+ max_scroll_x = max_line_width - text_box_x;
+
+ widget_init(&text_widget, text_box_y + 2, text_box_x + 2,
+ SCREEN_CENTER, SCREEN_CENTER, widget_attrs, WIDGET_BORDER);
+ mvwprintw(text_widget.window, 0, (text_box_x + 2 - get_mbs_width(title) - 2) / 2, " %s ", title);
+
+ if (current_top > max_scroll_y)
+ current_top = max_scroll_y;
+ if (current_left > max_scroll_x)
+ current_left = max_scroll_x;
+ update_text_lines();
+ update_y_scroll_bar();
+ update_x_scroll_bar();
+ return TRUE;
+}
+
+static void on_window_size_changed(void)
+{
+ create();
+}
+
+static void on_close(void)
+{
+ unsigned int i;
+
+ for (i = 0; i < text_lines_count; ++i)
+ free(text_lines[i]);
+ free(text_lines);
+ widget_free(&text_widget);
+}
+
+static struct widget text_widget = {
+ .handle_key = on_handle_key,
+ .window_size_changed = on_window_size_changed,
+ .close = on_close,
+};
+
+static void create_text_box(const char *const *lines, unsigned int count,
+ const char *title_, int attrs)
+{
+ unsigned int i;
+
+ text_lines = ccalloc(count, sizeof *text_lines);
+ for (i = 0; i < count; ++i)
+ text_lines[i] = cstrdup(lines[i]);
+ text_lines_count = count;
+ max_line_width = get_max_mbs_width(lines, count);
+ title = cstrdup(title_);
+ widget_attrs = attrs;
+
+ current_top = 0;
+ current_left = 0;
+
+ create();
+}