Notcurses 3.0.13
a blingful library for TUIs and character graphics
Loading...
Searching...
No Matches
reader.c
Go to the documentation of this file.
1#include "internal.h"
2
3static void
4ncreader_destroy_internal(ncreader* n){
5 if(n){
6 if(n->manage_cursor){
8 }
9 if(ncplane_set_widget(n->ncp, NULL, NULL) == 0){
10 ncplane_destroy(n->ncp);
11 }
12 ncplane_destroy(n->textarea);
13 free(n);
14 }
15}
16
17void ncreader_destroy(ncreader* n, char** contents){
18 if(n){
19 if(contents){
20 *contents = ncreader_contents(n);
21 }
22 ncreader_destroy_internal(n);
23 }
24}
25
27 ncreader_options zeroed = {0};
28 if(!opts){
29 opts = &zeroed;
30 }
32 logwarn("provided unsupported flags %016" PRIx64, opts->flags);
33 }
34 ncreader* nr = malloc(sizeof(*nr));
35 if(nr == NULL){
37 return NULL;
38 }
39 nr->ncp = n;
40 // do *not* bind it to the visible plane; we always want it offscreen,
41 // to the upper left of the true origin
42 struct ncplane_options nopts = {
43 .y = -ncplane_dim_y(n),
44 .x = -ncplane_dim_x(n),
45 .rows = ncplane_dim_y(n),
46 .cols = ncplane_dim_x(n),
47 .name = "text",
48 };
51 free(nr);
52 return NULL;
53 }
54
56 nr->xproject = 0;
57 nr->tchannels = opts->tchannels;
58 nr->tattrs = opts->tattrword;
61 ncplane_set_channels(nr->ncp, opts->tchannels);
62 ncplane_set_styles(nr->ncp, opts->tattrword);
63 if(ncplane_set_widget(n, nr, (void(*)(void*))ncreader_destroy_internal)){
66 free(nr);
67 return NULL;
68 }
69 return nr;
70}
71
72// empty both planes of all input, and home the cursors.
74 ncplane_erase(n->ncp);
75 ncplane_erase(n->textarea);
76 n->xproject = 0;
77 return 0;
78}
79
81 return n->ncp;
82}
83
84// copy the viewed area down from the textarea
85static int
86ncreader_redraw(ncreader* n){
87 int ret = 0;
88//fprintf(stderr, "redraw: xproj %d\n", n->xproject);
89//notcurses_debug(n->ncp->nc, stderr);
90 assert(n->xproject >= 0);
91 assert(n->textarea->lenx >= n->ncp->lenx);
92 assert(n->textarea->leny >= n->ncp->leny);
93 for(unsigned y = 0 ; y < n->ncp->leny ; ++y){
94 const unsigned texty = y;
95 for(unsigned x = 0 ; x < n->ncp->lenx ; ++x){
96 const unsigned textx = x + n->xproject;
97 const nccell* src = &n->textarea->fb[nfbcellidx(n->textarea, texty, textx)];
98 nccell* dst = &n->ncp->fb[nfbcellidx(n->ncp, y, x)];
99//fprintf(stderr, "projecting %d/%d [%s] to %d/%d [%s]\n", texty, textx, cell_extended_gcluster(n->textarea, src), y, x, cell_extended_gcluster(n->ncp, dst));
100 if(cellcmp_and_dupfar(&n->ncp->pool, dst, n->textarea, src) < 0){
101 ret = -1;
102 }
103 }
104 }
105 if(notcurses_cursor_enable(ncplane_notcurses(n->ncp), n->ncp->absy + n->ncp->y, n->ncp->absx + n->ncp->x)){
106 ret = -1;
107 }
108 return ret;
109}
110
111// try to move left. does not move past the start of the textarea, but will
112// try to move up and to the end of the previous row if not on the top row.
113// if on the left side of the viewarea, but not the left side of the textarea,
114// scrolls left. returns 0 if a move was made.
116 int viewx = n->ncp->x;
117 int textx = n->textarea->x;
118 int y = n->ncp->y;
119//fprintf(stderr, "moving left: tcurs: %dx%d vcurs: %dx%d xproj: %d\n", y, textx, y, viewx, n->xproject);
120 if(textx == 0){
121 // are we on the first column of the textarea? if so, we must also be on
122 // the first column of the viewarea. try to move up.
123 if(y == 0){
124 return -1; // no move possible
125 }
126 viewx = n->ncp->lenx - 1; // FIXME find end of particular row
127 --y;
128 textx = n->textarea->lenx - 1;
129 n->xproject = n->textarea->x - n->ncp->x;
130 }else{
131 // if we're on the first column of the viewarea, but not the first column
132 // of the textarea, we must be able to scroll to the left. do so.
133 // if we're not on the last column anywhere, move cursor right everywhere.
134 if(viewx == 0){
135 --n->xproject;
136 }else{
137 --viewx;
138 }
139 --textx;
140 }
141 ncplane_cursor_move_yx(n->textarea, y, textx);
142 ncplane_cursor_move_yx(n->ncp, y, viewx);
143//fprintf(stderr, "moved left: tcurs: %dx%d vcurs: %dx%d xproj: %d\n", y, textx, y, viewx, n->xproject);
144 ncreader_redraw(n);
145 return 0;
146}
147
148// try to move right. does not move past the end of the textarea, but will
149// try to move down and to the start of the previous row if not on the bottom
150// row. if on the right side of the viewarea, but not the right side of the
151// textarea, pans right. returns 0 if a move was made.
153 unsigned textx = n->textarea->x;
154 unsigned y = n->ncp->y;
155 unsigned viewx = n->ncp->x;
156//fprintf(stderr, "moving right: tcurs: %dx%d vcurs: %dx%d xproj: %d\n", y, textx, y, viewx, n->xproject);
157 if(textx >= n->textarea->lenx - 1){
158 // are we on the last column of the textarea? if so, we must also be on
159 // the first column of the viewarea. try to move down.
160 if(y >= n->textarea->leny - 1){
161 return -1; // no move possible
162 }
163 viewx = 0;
164 ++y;
165 textx = viewx;
166 n->xproject = 0;
167 }else{
168 // if we're on the first column of the viewarea, but not the first column
169 // of the textarea, we must be able to scroll to the left. do so.
170 // if we're not on the last column anywhere, move cursor right everywhere.
171 if(viewx >= n->ncp->lenx - 1){
172 ++n->xproject;
173 }else{
174 ++viewx;
175 }
176 ++textx;
177 }
178 ncplane_cursor_move_yx(n->textarea, y, textx);
179 ncplane_cursor_move_yx(n->ncp, y, viewx);
180//fprintf(stderr, "moved right: tcurs: %dx%d vcurs: %dx%d xproj: %d\n", y, textx, y, viewx, n->xproject);
181 ncreader_redraw(n);
182 return 0;
183}
184
185// try to move up. does not move past the top of the textarea.
186// returns 0 if a move was made.
188 int y = n->ncp->y;
189 if(y == 0){
190 // are we on the last row of the textarea? if so, we can't move.
191 return -1;
192 }
193 --y;
194 ncplane_cursor_move_yx(n->textarea, y, -1);
195 ncplane_cursor_move_yx(n->ncp, y, -1);
196 ncreader_redraw(n);
197 return 0;
198}
199
200// try to move down. does not move past the bottom of the textarea.
201// returns 0 if a move was made.
203 unsigned y = n->ncp->y;
204 if(y >= n->textarea->leny - 1){
205 // are we on the last row of the textarea? if so, we can't move.
206 return -1;
207 }
208 ++y;
209 ncplane_cursor_move_yx(n->textarea, y, -1);
210 ncplane_cursor_move_yx(n->ncp, y, -1);
211 ncreader_redraw(n);
212 return 0;
213}
214
215// only writing can enlarge the textarea. movement can pan, but not enlarge.
216int ncreader_write_egc(ncreader* n, const char* egc){
217 const int cols = ncstrwidth(egc, NULL, NULL);
218 if(cols < 0){
219 logerror("fed illegal UTF-8 [%s]", egc);
220 return -1;
221 }
222 if(n->textarea->x >= n->textarea->lenx - cols){
223 if(n->horscroll){
224 if(ncplane_resize_simple(n->textarea, n->textarea->leny, n->textarea->lenx + cols)){
225 return -1;
226 }
227 ++n->xproject;
228 }
229 }else if(n->ncp->x >= n->ncp->lenx){
230 ++n->xproject;
231 }
232 // use ncplane_putegc on both planes because it'll get cursor movement right
233 if(ncplane_putegc(n->textarea, egc, NULL) < 0){
234 return -1;
235 }
236 if(ncplane_putegc(n->ncp, egc, NULL) < 0){
237 return -1;
238 }
239 if(n->textarea->x >= n->textarea->lenx - cols){
240 if(!n->horscroll){
241 n->textarea->x = n->textarea->lenx - cols;
242 }
243 }
244 if(n->ncp->x >= n->ncp->lenx - cols){
245 n->ncp->x = n->ncp->lenx - cols;
246 }
247 ncreader_redraw(n);
248 return 0;
249}
250
251static bool
252do_backspace(ncreader* n){
253 int x = n->textarea->x;
254 int y = n->textarea->y;
255 if(n->textarea->x == 0){
256 if(n->textarea->y){
257 y = n->textarea->y - 1;
258 x = n->textarea->lenx - 1;
259 }
260 }else{
261 --x;
262 }
263 ncplane_putegc_yx(n->textarea, y, x, "", NULL);
264 ncplane_cursor_move_yx(n->textarea, y, x);
265 ncplane_cursor_move_yx(n->ncp, n->ncp->y, n->ncp->x - 1);
266 ncreader_redraw(n);
267 return true;
268}
269
270static bool
271is_egc_wordbreak(ncplane* textarea){
272 char* egc = ncplane_at_yx(textarea, textarea->y, textarea->x, NULL, NULL);
273 if(egc == NULL){
274 return true;
275 }
276 wchar_t w;
277 mbstate_t mbstate;
278 memset(&mbstate, 0, sizeof(mbstate));
279 size_t s = mbrtowc(&w, egc, MB_CUR_MAX, &mbstate);
280 free(egc);
281 if(s == (size_t)-1 || s == (size_t)-2){
282 return true;
283 }
284 if(iswordbreak(w)){
285 return true;
286 }
287 return false;
288}
289
290static bool
291ncreader_ctrl_input(ncreader* n, const ncinput* ni){
292 switch(ni->id){
293 case 'B':
295 break;
296 case 'F':
298 break;
299 case 'A': // cursor to beginning of line
300 while(n->textarea->x){
302 break;
303 }
304 }
305 break;
306 case 'E': // cursor to end of line
307 while(n->textarea->x < ncplane_dim_x(n->textarea) - 1){
309 break;
310 }
311 }
312 break;
313 case 'U': // clear line before cursor
314 while(n->textarea->x){
315 do_backspace(n);
316 }
317 break;
318 case 'W': // clear word before cursor
319 while(n->textarea->x){
321 break;
322 }
323 if(is_egc_wordbreak(n->textarea)){
324 break;
325 }
327 break;
328 }
329 do_backspace(n);
330 }
331 break;
332 default:
333 return false; // pass on all other ctrls
334 }
335 return true;
336}
337
338static bool
339ncreader_alt_input(ncreader* n, const ncinput* ni){
340 switch(ni->id){
341 case 'b': // back one word (to first cell), but not to previous line
342 while(n->textarea->x){
344 break;
345 }
346 if(is_egc_wordbreak(n->textarea)){
347 break;
348 }
349 }
350 break;
351 case 'f': // forward one word (past end cell)
352 while(n->textarea->x < ncplane_dim_x(n->textarea) - 1){
354 break;
355 }
356 if(is_egc_wordbreak(n->textarea)){
357 break;
358 }
359 }
360 break;
361 default:
362 return false;
363 }
364 return true;
365}
366
367// we pass along:
368// * anything with Alt
369// * anything with Ctrl, except 'U' (which clears all input)
370// * anything synthesized, save arrow keys and backspace
372 if(ni->evtype == NCTYPE_RELEASE){
373 return false;
374 }
375 if(ncinput_ctrl_p(ni) && !n->no_cmd_keys){
376 return ncreader_ctrl_input(n, ni);
377 }else if(ncinput_alt_p(ni) && !n->no_cmd_keys){
378 return ncreader_alt_input(n, ni);
379 }
380 if(ncinput_alt_p(ni) || ncinput_ctrl_p(ni)){ // pass on all alts/ctrls if no_cmd_keys is set
381 return false;
382 }
383 if(ni->id == NCKEY_BACKSPACE){
384 return do_backspace(n);
385 }
386 // FIXME deal with multicolumn EGCs -- probably extract these and make them
387 // general ncplane_cursor_{left, right, up, down}()
388 if(ni->id == NCKEY_LEFT){
390 return true;
391 }else if(ni->id == NCKEY_RIGHT){
393 return true;
394 }else if(ni->id == NCKEY_UP){
396 return true;
397 }else if(ni->id == NCKEY_DOWN){
399 return true;
400 }else if(nckey_synthesized_p(ni->id)){
401 return false;
402 }
403
404 for (int c=0; ni->eff_text[c]!=0; c++){
405 unsigned char egc[5]={0};
406 if(notcurses_ucs32_to_utf8(&ni->eff_text[c], 1, egc, 4)>=0){
407 ncreader_write_egc(n, (char*)egc);
408 }
409 }
410 return true;
411}
412
414 return ncplane_contents(n->ncp, 0, 0, 0, 0);
415}
const char * egc
Definition egcpool.h:173
assert(false)
const nccell * c
Definition egcpool.h:296
free(duplicated)
#define logerror(fmt,...)
Definition logging.h:32
#define logwarn(fmt,...)
Definition logging.h:37
#define NCKEY_UP
Definition nckeys.h:37
#define NCKEY_BACKSPACE
Definition nckeys.h:43
#define NCKEY_DOWN
Definition nckeys.h:39
#define NCKEY_RIGHT
Definition nckeys.h:38
#define NCKEY_LEFT
Definition nckeys.h:40
int ncplane_cursor_move_yx(ncplane *n, int y, int x)
Definition notcurses.c:720
char * ncplane_contents(ncplane *nc, int begy, int begx, unsigned leny, unsigned lenx)
Definition notcurses.c:3228
ncplane * notcurses_stdplane(notcurses *nc)
Definition notcurses.c:699
void ncplane_set_channels(ncplane *n, uint64_t channels)
Definition notcurses.c:1494
int notcurses_ucs32_to_utf8(const uint32_t *ucs32, unsigned ucs32count, unsigned char *resultbuf, size_t buflen)
Definition notcurses.c:3301
void ncplane_set_styles(ncplane *n, unsigned stylebits)
Definition notcurses.c:2071
int ncplane_destroy(ncplane *ncp)
Definition notcurses.c:1018
char * ncplane_at_yx(const ncplane *n, int y, int x, uint16_t *stylemask, uint64_t *channels)
Definition notcurses.c:214
int ncplane_putegc_yx(ncplane *n, int y, int x, const char *gclust, size_t *sbytes)
Definition notcurses.c:1995
int ncstrwidth(const char *egcs, int *validbytes, int *validwidth)
Definition notcurses.c:3309
notcurses * ncplane_notcurses(const ncplane *n)
Definition notcurses.c:2626
ncplane * ncplane_create(ncplane *n, const ncplane_options *nopts)
Definition notcurses.c:707
void ncplane_erase(ncplane *n)
Definition notcurses.c:2458
int y
Definition notcurses.h:1905
const struct ncplane_options * opts
Definition notcurses.h:3483
#define NCREADER_OPTION_NOCMDKEYS
Definition notcurses.h:4615
@ NCTYPE_RELEASE
Definition notcurses.h:1198
vopts n
Definition notcurses.h:3502
#define NCREADER_OPTION_HORSCROLL
Definition notcurses.h:4611
int int x
Definition notcurses.h:1905
#define NCREADER_OPTION_CURSOR
Definition notcurses.h:4618
int ncreader_move_up(ncreader *n)
Definition reader.c:187
int ncreader_move_down(ncreader *n)
Definition reader.c:202
ncreader * ncreader_create(ncplane *n, const ncreader_options *opts)
Definition reader.c:26
int ncreader_write_egc(ncreader *n, const char *egc)
Definition reader.c:216
char * ncreader_contents(const ncreader *n)
Definition reader.c:413
void ncreader_destroy(ncreader *n, char **contents)
Definition reader.c:17
ncplane * ncreader_plane(ncreader *n)
Definition reader.c:80
int ncreader_clear(ncreader *n)
Definition reader.c:73
int ncreader_move_right(ncreader *n)
Definition reader.c:152
bool ncreader_offer_input(ncreader *n, const ncinput *ni)
Definition reader.c:371
int ncreader_move_left(ncreader *n)
Definition reader.c:115
int notcurses_cursor_enable(notcurses *nc, int y, int x)
Definition render.c:1738
int notcurses_cursor_disable(notcurses *nc)
Definition render.c:1772
ncintype_e evtype
Definition notcurses.h:1218
uint32_t eff_text[NCINPUT_MAX_EFF_TEXT_CODEPOINTS]
Definition notcurses.h:1221
uint32_t id
Definition notcurses.h:1210
unsigned x
Definition internal.h:79
unsigned y
Definition internal.h:79
uint64_t tchannels
Definition internal.h:215
bool no_cmd_keys
Definition internal.h:220
bool horscroll
Definition internal.h:219
ncplane * ncp
Definition internal.h:214
ncplane * textarea
Definition internal.h:217
int xproject
Definition internal.h:218
uint32_t tattrs
Definition internal.h:216
bool manage_cursor
Definition internal.h:221
return NULL
Definition termdesc.h:229