Notcurses 3.0.16
a blingful library for TUIs and character graphics
Loading...
Searching...
No Matches
tabbed.c
Go to the documentation of this file.
1#include "internal.h"
2
3typedef struct nctabbed_opsint {
4 uint64_t selchan; // channel for the selected tab header
5 uint64_t hdrchan; // channel for unselected tab headers
6 uint64_t sepchan; // channel for the tab separator
7 char* separator; // separator string (copied by nctabbed_create())
8 uint64_t flags; // bitmask of NCTABBED_OPTION_*
10
11typedef struct nctabbed {
12 ncplane* ncp; // widget ncplane
13 ncplane* p; // tab content ncplane
14 ncplane* hp; // tab headers ncplane
15 // a doubly-linked circular list of tabs
16 nctab* leftmost; // the tab most to the left
17 nctab* selected; // the currently selected tab
18 int tabcount; // tab separator (can be NULL)
19 int sepcols; // separator with in columns
20 nctabbed_opsint opts; // copied in nctabbed_create()
22
24 nctab* t;
25 unsigned drawn_cols = 0;
26 unsigned rows, cols;
27 if(nt->tabcount == 0){
28 // no tabs = nothing to draw
29 ncplane_erase(nt->hp);
30 return;
31 }
32 // update sizes for planes
33 ncplane_dim_yx(nt->ncp, &rows, &cols);
35 ncplane_resize_simple(nt->hp, -1, cols);
36 ncplane_resize_simple(nt->p, rows - 1, cols);
37 ncplane_move_yx(nt->hp, rows - 2, 0);
38 }else{
39 ncplane_resize_simple(nt->hp, -1, cols);
40 ncplane_resize_simple(nt->p, rows - 1, cols);
41 }
42 // the callback draws the tab contents
43 if(nt->selected->cb){
44 nt->selected->cb(nt->selected, nt->p, nt->selected->curry);
45 }
46 // now we draw the headers
47 t = nt->leftmost;
48 ncplane_erase(nt->hp);
50 do{
51 if(t == nt->selected){
53 drawn_cols += ncplane_putstr(nt->hp, t->name);
55 }else{
56 drawn_cols += ncplane_putstr(nt->hp, t->name);
57 }
58 // avoid drawing the separator after the last tab, or when we
59 // ran out of space, or when it's not set
60 if((t->next != nt->leftmost || drawn_cols >= cols) && nt->opts.separator){
62 drawn_cols += ncplane_putstr(nt->hp, nt->opts.separator);
64 }
65 t = t->next;
66 }while(t != nt->leftmost && drawn_cols < cols);
67}
68
70 nctab* t = nt->leftmost;
71 int cols = ncplane_dim_x(nt->hp);
72 int takencols = 0;
73 if(!t){
74 return;
75 }
76//fprintf(stderr, "ensuring selected header visible\n");
77 do{
78 if(t == nt->selected){
79 break;
80 }
81 takencols += t->namecols + nt->sepcols;
82 if(takencols >= cols){
83//fprintf(stderr, "not enough space, rotating\n");
84 takencols -= nt->leftmost->namecols + nt->sepcols;
85 nctabbed_rotate(nt, -1);
86 }
87 t = t->next;
88//fprintf(stderr, "iteration over: takencols = %d, cols = %d\n", takencols, cols);
89 }while(t != nt->leftmost);
90//fprintf(stderr, "ensuring done\n");
91}
92
93static bool
94nctabbed_validate_opts(const nctabbed_options* opts){
96 logwarn("provided unsupported flags 0x%016" PRIx64, opts->flags);
97 }
98 if(opts->sepchan && !opts->separator){
99 logwarn("provided non-zero separator channel when separator is NULL")
100 }
101 return true;
102}
103
105 return nt->selected;
106}
107
109 return nt->leftmost;
110}
111
113 return nt->tabcount;
114}
115
117 return nt->ncp;
118}
119
121 return nt->p;
122}
123
125 return t->cb;
126}
127
128const char* nctab_name(nctab* t){
129 return t->name;
130}
131
133 return t->namecols;
134}
135
137 return t->curry;
138}
139
141 return t->next;
142}
143
145 return t->prev;
146}
147
149 nctabbed_options zeroed = {0};
150 ncplane_options nopts = {0};
151 unsigned nrows, ncols;
152 nctabbed* nt = NULL;
153 if(!topts){
154 topts = &zeroed;
155 }
156 if(!nctabbed_validate_opts(topts)){
157 goto err;
158 }
159 if((nt = malloc(sizeof(*nt))) == NULL){
160 logerror("Couldn't allocate nctabbed");
161 goto err;
162 }
163 nt->ncp = n;
164 nt->leftmost = nt->selected = NULL;
165 nt->tabcount = 0;
166 nt->sepcols = 0;
167 nt->opts.separator = NULL;
168 nt->opts.selchan = topts->selchan;
169 nt->opts.hdrchan = topts->hdrchan;
170 nt->opts.sepchan = topts->sepchan;
171 nt->opts.flags = topts->flags;
172 if(topts->separator){
173 if((nt->sepcols = ncstrwidth(topts->separator, NULL, NULL)) < 0){
174 logerror("Separator string contains illegal characters");
175 goto err;
176 }
177 if((nt->opts.separator = strdup(topts->separator)) == NULL){
178 logerror("Couldn't allocate nctabbed separator");
179 goto err;
180 }
181 }
182 ncplane_dim_yx(n, &nrows, &ncols);
183 if(topts->flags & NCTABBED_OPTION_BOTTOM){
184 nopts.y = nopts.x = 0;
185 nopts.cols = ncols;
186 nopts.rows = nrows - 1;
187 if((nt->p = ncplane_create(n, &nopts)) == NULL){
188 logerror("Couldn't create the tab content plane");
189 goto err;
190 }
191 nopts.y = nrows - 2;
192 nopts.rows = 1;
193 if((nt->hp = ncplane_create(n, &nopts)) == NULL){
194 logerror("Couldn't create the tab headers plane");
195 ncplane_destroy(nt->p);
196 goto err;
197 }
198 }else{
199 nopts.y = nopts.x = 0;
200 nopts.cols = ncols;
201 nopts.rows = 1;
202 if((nt->hp = ncplane_create(n, &nopts)) == NULL){
203 logerror("Couldn't create the tab headers plane");
204 goto err;
205 }
206 nopts.y = 1;
207 nopts.rows = nrows - 1;
208 if((nt->p = ncplane_create(n, &nopts)) == NULL){
209 logerror("Couldn't create the tab content plane");
210 ncplane_destroy(nt->hp);
211 goto err;
212 }
213 }
214 if(ncplane_set_widget(nt->ncp, nt, (void(*)(void*))nctabbed_destroy)){
215 ncplane_destroy(nt->hp);
216 ncplane_destroy(nt->p);
217 goto err;
218 }
219 nctabbed_redraw(nt);
220 return nt;
221
222err:
224 if(nt){
225 free(nt->opts.separator);
226 free(nt);
227 }
228 return NULL;
229}
230
231nctab* nctabbed_add(nctabbed* nt, nctab* after, nctab* before, tabcb cb,
232 const char* name, void* opaque){
233 nctab* t;
234 if(after && before){
235 if(after->next != before || before->prev != after){
236 logerror("bad before (%p) / after (%p) spec", before, after);
237 return NULL;
238 }
239 }else if(!after && !before){
240 // add it to the right of the selected tab
241 after = nt->selected;
242 }
243 if((t = malloc(sizeof(*t))) == NULL){
244 logerror("Couldn't allocate nctab")
245 return NULL;
246 }
247 if((t->name = strdup(name)) == NULL){
248 logerror("Couldn't allocate the tab name");
249 free(t);
250 return NULL;
251 }
252 if((t->namecols = ncstrwidth(name, NULL, NULL)) < 0){
253 logerror("Tab name contains illegal characters")
254 free(t->name);
255 free(t);
256 return NULL;
257 }
258 if(after){
259 t->next = after->next;
260 t->prev = after;
261 after->next = t;
262 t->next->prev = t;
263 }else if(before){
264 t->next = before;
265 t->prev = before->prev;
266 before->prev = t;
267 t->prev->next = t;
268 }else{
269 // the first tab
270 t->prev = t->next = t;
271 nt->leftmost = nt->selected = t;
272 }
273 t->nt = nt;
274 t->cb = cb;
275 t->curry = opaque;
276 ++nt->tabcount;
277 return t;
278}
279
281 if(!t){
282 logerror("Provided NULL nctab");
283 return -1;
284 }
285 if(nt->tabcount == 1){
286 nt->leftmost = nt->selected = NULL;
287 }else{
288 if(nt->selected == t){
289 nt->selected = t->next;
290 }
291 if(nt->leftmost == t){
292 nt->leftmost = t->next;
293 }
294 t->next->prev = t->prev;
295 t->prev->next = t->next;
296 }
297 free(t->name);
298 free(t);
299 --nt->tabcount;
300 return 0;
301}
302
303int nctab_move(nctabbed* nt __attribute__ ((unused)), nctab* t, nctab* after, nctab* before){
304 if(after && before){
305 if(after->prev != before || before->next != after){
306 logerror("bad before (%p) / after (%p) spec", before, after);
307 return -1;
308 }
309 }else if(!after && !before){
310 logerror("bad before (%p) / after (%p) spec", before, after);
311 return -1;
312 }
313 // bad things would happen
314 if(t == after || t == before){
315 logerror("Cannot move a tab before or after itself.");
316 return -1;
317 }
318 t->prev->next = t->next;
319 t->next->prev = t->prev;
320 if(after){
321 t->next = after->next;
322 t->prev = after;
323 after->next = t;
324 t->next->prev = t;
325 }else{
326 t->next = before;
327 t->prev = before->prev;
328 before->prev = t;
329 t->prev->next = t;
330 }
331 return 0;
332}
333
335 if(t == nt->leftmost->prev){
336 nctab_move(nt, t, NULL, nt->leftmost);
337 nt->leftmost = t;
338 return;
339 }else if(t == nt->leftmost){
340 nt->leftmost = t->next;
341 }
342 nctab_move(nt, t, t->next, NULL);
343}
344
346 if(t == nt->leftmost){
347 nt->leftmost = t->next;
348 nctab_move(nt, t, nt->leftmost->prev, NULL);
349 return;
350 }else if(t == nt->leftmost->next){
351 nt->leftmost = t;
352 }
353 nctab_move(nt, t, NULL, t->prev);
354}
355
356void nctabbed_rotate(nctabbed* nt, int amt){
357 if(amt > 0){
358 for(int i = 0 ; i < amt ; ++i){
359 nt->leftmost = nt->leftmost->prev;
360 }
361 }else{
362 for(int i = 0 ; i < -amt ; ++i){
363 nt->leftmost = nt->leftmost->next;
364 }
365 }
366}
367
369 if(nt->tabcount == 0){
370 return NULL;
371 }
372 nt->selected = nt->selected->next;
373 return nt->selected;
374}
375
377 if(nt->tabcount == 0){
378 return NULL;
379 }
380 nt->selected = nt->selected->prev;
381 return nt->selected;
382}
383
385 nctab* prevsel = nt->selected;
386 nt->selected = t;
387 return prevsel;
388}
389
390void nctabbed_channels(nctabbed* nt, uint64_t* RESTRICT hdrchan,
391 uint64_t* RESTRICT selchan, uint64_t* RESTRICT sepchan){
392 if(hdrchan){
393 memcpy(hdrchan, &nt->opts.hdrchan, sizeof(*hdrchan));
394 }
395 if(selchan){
396 memcpy(selchan, &nt->opts.selchan, sizeof(*selchan));
397 }
398 if(sepchan){
399 memcpy(sepchan, &nt->opts.sepchan, sizeof(*sepchan));
400 }
401}
402
404 return nt->opts.separator;
405}
406
408 return nt->sepcols;
409}
410
412 if(!nt){
413 return;
414 }
415 if(ncplane_set_widget(nt->ncp, NULL, NULL) == 0){
416 nctab* t = nt->leftmost;
417 nctab* tmp;
418 if(t){
419 t->prev->next = NULL;
420 if(t->next){
421 t->next->prev = NULL;
422 }
423 }
424 while(t){
425 tmp = t->next;
426 free(t->name);
427 free(t);
428 t = tmp;
429 }
431 free(nt->opts.separator);
432 free(nt);
433 }
434}
435
436void nctabbed_set_hdrchan(nctabbed* nt, uint64_t chan){
437 nt->opts.hdrchan = chan;
438}
439
440void nctabbed_set_selchan(nctabbed* nt, uint64_t chan){
441 nt->opts.selchan = chan;
442}
443
444void nctabbed_set_sepchan(nctabbed* nt, uint64_t chan){
445 nt->opts.sepchan = chan;
446}
447
449 tabcb prevcb = t->cb;
450 t->cb = newcb;
451 return prevcb;
452}
453
454int nctab_set_name(nctab* t, const char* newname){
455 int newnamecols;
456 char* prevname = t->name;
457 if((newnamecols = ncstrwidth(newname, NULL, NULL)) < 0){
458 logerror("New tab name contains illegal characters");
459 return -1;
460 }
461 if((t->name = strdup(newname)) == NULL){
462 logerror("Couldn't allocate new tab name");
463 t->name = prevname;
464 return -1;
465 }
466 free(prevname);
467 t->namecols = newnamecols;
468 return 0;
469}
470
471void* nctab_set_userptr(nctab* t, void* newopaque){
472 void* prevcurry = t->curry;
473 t->curry = newopaque;
474 return prevcurry;
475}
476
477int nctabbed_set_separator(nctabbed* nt, const char* separator){
478 int newsepcols;
479 char* prevsep = nt->opts.separator;
480 if((newsepcols = ncstrwidth(separator, NULL, NULL)) < 0){
481 logerror("New tab separator contains illegal characters");
482 return -1;
483 }
484 if((nt->opts.separator = strdup(separator)) == NULL){
485 logerror("Couldn't allocate new tab separator");
486 nt->opts.separator = prevsep;
487 return -1;
488 }
489 free(prevsep);
490 nt->sepcols = newsepcols;
491 return 0;
492}
__attribute__((nonnull(1)))
Definition egcpool.c:4
#define logerror(fmt,...)
Definition logging.h:32
#define logwarn(fmt,...)
Definition logging.h:37
void ncplane_set_channels(ncplane *n, uint64_t channels)
Definition notcurses.c:1497
int ncplane_destroy(ncplane *ncp)
Definition notcurses.c:1021
int ncplane_move_yx(ncplane *n, int y, int x)
Definition notcurses.c:2417
int ncstrwidth(const char *egcs, int *validbytes, int *validwidth)
Definition notcurses.c:3311
ncplane * ncplane_create(ncplane *n, const ncplane_options *nopts)
Definition notcurses.c:710
void ncplane_erase(ncplane *n)
Definition notcurses.c:2464
int ncplane_family_destroy(ncplane *ncp)
Definition notcurses.c:1071
void ncplane_dim_yx(const ncplane *n, unsigned *rows, unsigned *cols)
Definition notcurses.c:304
void(* tabcb)(struct nctab *t, struct ncplane *ncp, void *curry)
Definition notcurses.h:4254
const struct ncplane_options * opts
Definition notcurses.h:3487
vopts n
Definition notcurses.h:3506
#define RESTRICT
Definition notcurses.h:24
#define NCTABBED_OPTION_BOTTOM
Definition notcurses.h:4241
char * name
Definition internal.h:234
struct nctabbed * nt
Definition internal.h:232
tabcb cb
Definition internal.h:233
struct nctab * prev
Definition internal.h:237
void * curry
Definition internal.h:236
int namecols
Definition internal.h:235
struct nctab * next
Definition internal.h:238
uint64_t sepchan
Definition tabbed.c:6
char * separator
Definition tabbed.c:7
uint64_t flags
Definition tabbed.c:8
uint64_t selchan
Definition tabbed.c:4
uint64_t hdrchan
Definition tabbed.c:5
const char * separator
Definition notcurses.h:4247
ncplane * ncp
Definition tabbed.c:12
nctab * leftmost
Definition tabbed.c:16
ncplane * hp
Definition tabbed.c:14
nctab * selected
Definition tabbed.c:17
int sepcols
Definition tabbed.c:19
ncplane * p
Definition tabbed.c:13
int tabcount
Definition tabbed.c:18
nctabbed_opsint opts
Definition tabbed.c:20
int nctab_name_width(nctab *t)
Definition tabbed.c:132
void nctabbed_rotate(nctabbed *nt, int amt)
Definition tabbed.c:356
nctab * nctabbed_selected(nctabbed *nt)
Definition tabbed.c:104
void nctabbed_set_sepchan(nctabbed *nt, uint64_t chan)
Definition tabbed.c:444
int nctab_move(nctabbed *nt __attribute__((unused)), nctab *t, nctab *after, nctab *before)
Definition tabbed.c:303
nctab * nctabbed_prev(nctabbed *nt)
Definition tabbed.c:376
tabcb nctab_cb(nctab *t)
Definition tabbed.c:124
const char * nctabbed_separator(nctabbed *nt)
Definition tabbed.c:403
tabcb nctab_set_cb(nctab *t, tabcb newcb)
Definition tabbed.c:448
void nctab_move_left(nctabbed *nt, nctab *t)
Definition tabbed.c:345
int nctabbed_separator_width(nctabbed *nt)
Definition tabbed.c:407
nctab * nctabbed_select(nctabbed *nt, nctab *t)
Definition tabbed.c:384
void nctabbed_destroy(nctabbed *nt)
Definition tabbed.c:411
void nctabbed_set_hdrchan(nctabbed *nt, uint64_t chan)
Definition tabbed.c:436
nctab * nctab_next(nctab *t)
Definition tabbed.c:140
int nctabbed_del(nctabbed *nt, nctab *t)
Definition tabbed.c:280
void nctabbed_redraw(nctabbed *nt)
Definition tabbed.c:23
nctab * nctabbed_leftmost(nctabbed *nt)
Definition tabbed.c:108
void nctab_move_right(nctabbed *nt, nctab *t)
Definition tabbed.c:334
int nctabbed_set_separator(nctabbed *nt, const char *separator)
Definition tabbed.c:477
nctab * nctabbed_add(nctabbed *nt, nctab *after, nctab *before, tabcb cb, const char *name, void *opaque)
Definition tabbed.c:231
void nctabbed_channels(nctabbed *nt, uint64_t *RESTRICT hdrchan, uint64_t *RESTRICT selchan, uint64_t *RESTRICT sepchan)
Definition tabbed.c:390
nctabbed * nctabbed_create(ncplane *n, const nctabbed_options *topts)
Definition tabbed.c:148
void * nctab_set_userptr(nctab *t, void *newopaque)
Definition tabbed.c:471
ncplane * nctabbed_content_plane(nctabbed *nt)
Definition tabbed.c:120
ncplane * nctabbed_plane(nctabbed *nt)
Definition tabbed.c:116
int nctabbed_tabcount(nctabbed *nt)
Definition tabbed.c:112
void nctabbed_set_selchan(nctabbed *nt, uint64_t chan)
Definition tabbed.c:440
void nctabbed_ensure_selected_header_visible(nctabbed *nt)
Definition tabbed.c:69
void * nctab_userptr(nctab *t)
Definition tabbed.c:136
const char * nctab_name(nctab *t)
Definition tabbed.c:128
nctab * nctab_prev(nctab *t)
Definition tabbed.c:144
int nctab_set_name(nctab *t, const char *newname)
Definition tabbed.c:454
nctab * nctabbed_next(nctabbed *nt)
Definition tabbed.c:368
return NULL
Definition termdesc.h:229