ui/ncurses: Display multibyte strings correctly in textscreens
[petitboot] / ui / ncurses / nc-textscreen.c
1 /*
2  *  Copyright (C) 2013 IBM Corporation
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; version 2 of the License.
7  *
8  *  This program is distributed in the hope that it will be useful,
9  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  *  GNU General Public License for more details.
12  *
13  *  You should have received a copy of the GNU General Public License
14  *  along with this program; if not, write to the Free Software
15  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16  */
17
18 #if defined(HAVE_CONFIG_H)
19 #include "config.h"
20 #endif
21
22 #include <string.h>
23
24 #include <talloc/talloc.h>
25 #include <types/types.h>
26 #include <log/log.h>
27 #include <fold/fold.h>
28 #include <util/util.h>
29 #include <i18n/i18n.h>
30
31 #include "nc-cui.h"
32 #include "nc-textscreen.h"
33
34 struct text_screen *text_screen_from_scr(struct nc_scr *scr)
35 {
36         struct text_screen *text_screen;
37         assert(scr->sig == pb_text_screen_sig);
38         text_screen = container_of(scr, struct text_screen, scr);
39         return text_screen;
40 }
41
42 void text_screen_draw(struct text_screen *screen)
43 {
44         int max_y, max_x, i, len;
45
46         max_y = getmaxy(screen->scr.sub_ncw);
47         max_x = getmaxx(screen->scr.sub_ncw) - 1;
48
49         max_y = min(max_y, screen->scroll_y + screen->n_lines);
50
51         for (i = screen->scroll_y; i < max_y; i++) {
52                 len = strncols(screen->lines[i]) > max_x ? max_x : -1;
53                 mvwaddnstr(screen->scr.sub_ncw, i, 1, screen->lines[i], len);
54         }
55
56         wrefresh(screen->scr.sub_ncw);
57 }
58
59 static void text_screen_scroll(struct text_screen *screen, int key)
60 {
61         int win_lines = getmaxy(screen->scr.sub_ncw);
62         int win_cols = getmaxx(screen->scr.sub_ncw) - 1;
63         int delta, len, i;
64
65         if (key == KEY_UP)
66                 delta = -1;
67         else if (key == KEY_DOWN)
68                 delta = 1;
69         else
70                 return;
71
72         if (screen->scroll_y + delta < 0)
73                 return;
74         if (screen->scroll_y + delta + win_lines > screen->n_lines)
75                 return;
76
77         screen->scroll_y += delta;
78         wscrl(screen->scr.sub_ncw, delta);
79
80
81         if (delta > 0) {
82                 i = screen->scroll_y + win_lines - 1;
83                 len = strncols(screen->lines[i]) > win_cols ? win_cols : -1;
84                 mvwaddnstr(screen->scr.sub_ncw, win_lines - 1, 1,
85                                 screen->lines[i], len);
86         } else if (delta < 0) {
87                 i = screen->scroll_y;
88                 len = strncols(screen->lines[i]) > win_cols ? win_cols : -1;
89                 mvwaddnstr(screen->scr.sub_ncw, 0, 1, screen->lines[i], len);
90         }
91
92         wrefresh(screen->scr.sub_ncw);
93 }
94
95 void text_screen_clear(struct text_screen *screen)
96 {
97         talloc_free(screen->lines);
98         screen->n_lines = 0;
99         screen->n_alloc_lines = 16;
100         screen->lines = talloc_array(screen, const char *,
101                         screen->n_alloc_lines);
102 }
103
104 static void __text_screen_append_line(struct text_screen *screen,
105                 const char *line)
106 {
107         if (screen->n_lines == screen->n_alloc_lines) {
108                 screen->n_alloc_lines *= 2;
109                 screen->lines = talloc_realloc(screen, screen->lines,
110                                                 const char *,
111                                                 screen->n_alloc_lines);
112         }
113
114         screen->lines[screen->n_lines] = line;
115         screen->n_lines++;
116 }
117
118 void text_screen_append_line(struct text_screen *screen, const char *fmt, ...)
119 {
120         char *line;
121         va_list ap;
122
123         if (fmt) {
124                 va_start(ap, fmt);
125                 line = talloc_vasprintf(screen->lines, fmt, ap);
126                 va_end(ap);
127         } else {
128                 line = "";
129         }
130
131         __text_screen_append_line(screen, line);
132 }
133
134 static int text_screen_fold_cb(void *arg, const char *buf, int len)
135 {
136         struct text_screen *screen = arg;
137
138         buf = len ? talloc_strndup(screen->lines, buf, len) : "";
139         __text_screen_append_line(screen, buf);
140
141         return 0;
142 }
143
144 void text_screen_set_text(struct text_screen *screen, const char *text)
145 {
146         fold_text(text, getmaxx(screen->scr.sub_ncw) - 1, text_screen_fold_cb,
147                         screen);
148 }
149
150 void text_screen_process_key(struct nc_scr *scr, int key)
151 {
152         struct text_screen *screen = text_screen_from_scr(scr);
153
154         switch (key) {
155         case 'x':
156         case 27: /* esc */
157                 screen->on_exit(screen->cui);
158                 break;
159         case KEY_DOWN:
160         case KEY_UP:
161                 text_screen_scroll(screen, key);
162                 break;
163         case 'h':
164                 if (screen->help_text)
165                         cui_show_help(screen->cui, screen->help_title,
166                                         screen->help_text);
167                 break;
168         default:
169                 break;
170         }
171 }
172
173 static void text_screen_resize(struct nc_scr *scr)
174 {
175         struct text_screen *screen = text_screen_from_scr(scr);
176         text_screen_draw(screen);
177 }
178
179 struct nc_scr *text_screen_scr(struct text_screen *screen)
180 {
181         return &screen->scr;
182 }
183
184 void text_screen_set_help(struct text_screen *screen, const char *title,
185                 const struct help_text *text)
186 {
187         screen->help_title = title;
188         screen->help_text = text;
189         screen->scr.frame.help = _("x=exit, h=help");
190 }
191
192 static int text_screen_post(struct nc_scr *scr)
193 {
194         struct text_screen *screen = text_screen_from_scr(scr);
195
196         if (screen->need_update) {
197                 text_screen_draw(screen);
198                 screen->need_update = false;
199         }
200
201         nc_scr_frame_draw(scr);
202         redrawwin(scr->main_ncw);
203         wrefresh(scr->main_ncw);
204         return 0;
205 }
206
207 void text_screen_init(struct text_screen *screen, struct cui *cui,
208                 const char *title, void (*on_exit)(struct cui *))
209 {
210         nc_scr_init(&screen->scr, pb_text_screen_sig, 0,
211                         cui, text_screen_process_key,
212                         text_screen_post, NULL, text_screen_resize);
213
214         /* this will establish our array of lines */
215         screen->lines = NULL;
216         text_screen_clear(screen);
217
218         screen->cui = cui;
219         screen->on_exit = on_exit;
220         screen->need_update = false;
221
222         screen->scr.frame.ltitle = talloc_strdup(screen, title);
223         screen->scr.frame.rtitle = NULL;
224         screen->scr.frame.help = _("x=exit");
225         scrollok(screen->scr.sub_ncw, true);
226 }