@ -3,15 +3,18 @@
# include <ctype.h>
# include <form.h>
void term_util_init_colors ( ) {
void term_util_init_colors ( )
{
if ( has_colors ( ) ) {
// Enable color
start_color ( ) ;
use_default_colors ( ) ;
for ( int ifg = 0 ; ifg < Colors_count ; + + ifg ) {
for ( int ibg = 0 ; ibg < Colors_count ; + + ibg ) {
int res = init_pair ( ( short int ) ( 1 + ifg * Colors_count + ibg ) ,
( short int ) ( ifg - 1 ) , ( short int ) ( ibg - 1 ) ) ;
int res = init_pair (
( short int ) ( 1 + ifg * Colors_count + ibg ) ,
( short int ) ( ifg - 1 ) ,
( short int ) ( ibg - 1 ) ) ;
( void ) res ;
// Might fail on Linux virtual console/terminal for a couple of colors.
// Just ignore.
@ -29,8 +32,7 @@ void term_util_init_colors() {
}
# define ORCA_CONTAINER_OF(ptr, type, member) \
( ( type * ) ( ( char * ) ( 1 ? ( ptr ) : & ( ( type * ) 0 ) - > member ) - \
offsetof ( type , member ) ) )
( ( type * ) ( ( char * ) ( 1 ? ( ptr ) : & ( ( type * ) 0 ) - > member ) - offsetof ( type , member ) ) )
struct Qmsg {
Qblock qblock ;
@ -65,8 +67,12 @@ ORCA_NOINLINE static void qmenu_reprint(Qmenu *qm);
Qnav_stack qnav_stack ;
void qnav_init ( ) { qnav_stack = ( Qnav_stack ) { 0 } ; }
void qnav_deinit ( ) {
void qnav_init ( )
{
qnav_stack = ( Qnav_stack ) { 0 } ;
}
void qnav_deinit ( )
{
while ( qnav_stack . top )
qnav_stack_pop ( ) ;
}
@ -77,7 +83,8 @@ void qnav_deinit() {
// you've finished doing the rest of the setup on the Qblock. The y and x
// fields can be junk, though, since this function writes to them without
// reading them.
static ORCA_NOINLINE void qnav_reposition_block ( Qblock * qb ) {
static ORCA_NOINLINE void qnav_reposition_block ( Qblock * qb )
{
int top = 0 , left = 0 ;
Qblock * prev = qb - > down ;
if ( ! prev )
@ -114,7 +121,8 @@ done:
qb - > y = top ;
qb - > x = left ;
}
static ORCA_NOINLINE void qnav_stack_push ( Qblock * qb , int height , int width ) {
static ORCA_NOINLINE void qnav_stack_push ( Qblock * qb , int height , int width )
{
# ifndef NDEBUG
for ( Qblock * i = qnav_stack . top ; i ; i = i - > down ) {
assert ( i ! = qb ) ;
@ -133,14 +141,19 @@ static ORCA_NOINLINE void qnav_stack_push(Qblock *qb, int height, int width) {
qnav_stack . occlusion_dirty = true ;
}
Qblock * qnav_top_block ( ) { return qnav_stack . top ; }
Qblock * qnav_top_block ( )
{
return qnav_stack . top ;
}
void qblock_init ( Qblock * qb , Qblock_type_tag tag ) {
* qb = ( Qblock ) { 0 } ;
void qblock_init ( Qblock * qb , Qblock_type_tag tag )
{
* qb = ( Qblock ) { 0 } ;
qb - > tag = tag ;
}
void qnav_free_block ( Qblock * qb ) {
void qnav_free_block ( Qblock * qb )
{
switch ( qb - > tag ) {
case Qblock_type_qmsg : {
Qmsg * qm = qmsg_of ( qb ) ;
@ -156,7 +169,8 @@ void qnav_free_block(Qblock *qb) {
}
}
void qnav_stack_pop ( void ) {
void qnav_stack_pop ( void )
{
assert ( qnav_stack . top ) ;
if ( ! qnav_stack . top )
return ;
@ -178,7 +192,8 @@ void qnav_stack_pop(void) {
delwin ( outer_window ) ;
}
bool qnav_draw ( void ) {
bool qnav_draw ( void )
{
bool drew_any = false ;
if ( ! qnav_stack . bottom )
goto done ;
@ -227,7 +242,8 @@ done:
return drew_any ;
}
void qnav_adjust_term_size ( void ) {
void qnav_adjust_term_size ( void )
{
if ( ! qnav_stack . bottom )
return ;
for ( Qblock * qb = qnav_stack . bottom ; qb ; qb = qb - > up )
@ -235,13 +251,22 @@ void qnav_adjust_term_size(void) {
qnav_stack . occlusion_dirty = true ;
}
void qblock_print_border ( Qblock * qb , unsigned int attr ) {
wborder ( qb - > outer_window , ACS_VLINE | attr , ACS_VLINE | attr ,
ACS_HLINE | attr , ACS_HLINE | attr , ACS_ULCORNER | attr ,
ACS_URCORNER | attr , ACS_LLCORNER | attr , ACS_LRCORNER | attr ) ;
void qblock_print_border ( Qblock * qb , unsigned int attr )
{
wborder (
qb - > outer_window ,
ACS_VLINE | attr ,
ACS_VLINE | attr ,
ACS_HLINE | attr ,
ACS_HLINE | attr ,
ACS_ULCORNER | attr ,
ACS_URCORNER | attr ,
ACS_LLCORNER | attr ,
ACS_LRCORNER | attr ) ;
}
void qblock_print_title ( Qblock * qb , char const * title , int attr ) {
void qblock_print_title ( Qblock * qb , char const * title , int attr )
{
wmove ( qb - > outer_window , 0 , 1 ) ;
attr_t attrs = A_NORMAL ;
short pair = 0 ;
@ -253,9 +278,13 @@ void qblock_print_title(Qblock *qb, char const *title, int attr) {
wattr_set ( qb - > outer_window , attrs , pair , NULL ) ;
}
void qblock_set_title ( Qblock * qb , char const * title ) { qb - > title = title ; }
void qblock_set_title ( Qblock * qb , char const * title )
{
qb - > title = title ;
}
void qblock_print_frame ( Qblock * qb , bool active ) {
void qblock_print_frame ( Qblock * qb , bool active )
{
qblock_print_border ( qb , active ? A_NORMAL : A_DIM ) ;
if ( qb - > title ) {
qblock_print_title ( qb , qb - > title , active ? A_NORMAL : A_DIM ) ;
@ -268,19 +297,25 @@ void qblock_print_frame(Qblock *qb, bool active) {
}
}
WINDOW * qmsg_window ( Qmsg * qm ) { return qm - > qblock . content_window ; }
WINDOW * qmsg_window ( Qmsg * qm )
{
return qm - > qblock . content_window ;
}
void qmsg_set_title ( Qmsg * qm , char const * title ) {
void qmsg_set_title ( Qmsg * qm , char const * title )
{
qblock_set_title ( & qm - > qblock , title ) ;
}
void qmsg_set_dismiss_mode ( Qmsg * qm , Qmsg_dismiss_mode mode ) {
void qmsg_set_dismiss_mode ( Qmsg * qm , Qmsg_dismiss_mode mode )
{
if ( qm - > dismiss_mode = = mode )
return ;
qm - > dismiss_mode = mode ;
}
Qmsg * qmsg_push ( int height , int width ) {
Qmsg * qmsg_push ( int height , int width )
{
Qmsg * qm = malloc ( sizeof ( Qmsg ) ) ;
qblock_init ( & qm - > qblock , Qblock_type_qmsg ) ;
qm - > dismiss_mode = Qmsg_dismiss_mode_explicitly ;
@ -288,7 +323,8 @@ Qmsg *qmsg_push(int height, int width) {
return qm ;
}
Qmsg * qmsg_printf_push ( char const * title , char const * fmt , . . . ) {
Qmsg * qmsg_printf_push ( char const * title , char const * fmt , . . . )
{
int titlewidth = title ? ( int ) strlen ( title ) : 0 ;
va_list ap ;
va_start ( ap , fmt ) ;
@ -339,8 +375,9 @@ Qmsg *qmsg_printf_push(char const *title, char const *fmt, ...) {
return msg ;
}
bool qmsg_drive ( Qmsg * qm , int key , Qmsg_action * out_action ) {
* out_action = ( Qmsg_action ) { 0 } ;
bool qmsg_drive ( Qmsg * qm , int key , Qmsg_action * out_action )
{
* out_action = ( Qmsg_action ) { 0 } ;
Qmsg_dismiss_mode dm = qm - > dismiss_mode ;
switch ( dm ) {
case Qmsg_dismiss_mode_explicitly :
@ -364,9 +401,13 @@ bool qmsg_drive(Qmsg *qm, int key, Qmsg_action *out_action) {
return false ;
}
Qmsg * qmsg_of ( Qblock * qb ) { return ORCA_CONTAINER_OF ( qb , Qmsg , qblock ) ; }
Qmsg * qmsg_of ( Qblock * qb )
{
return ORCA_CONTAINER_OF ( qb , Qmsg , qblock ) ;
}
Qmenu * qmenu_create ( int id ) {
Qmenu * qmenu_create ( int id )
{
Qmenu * qm = ( Qmenu * ) malloc ( sizeof ( Qmenu ) ) ;
qblock_init ( & qm - > qblock , Qblock_type_qmenu ) ;
qm - > items = NULL ;
@ -378,9 +419,16 @@ Qmenu *qmenu_create(int id) {
qm - > is_frontmost = 0 ;
return qm ;
}
void qmenu_destroy ( Qmenu * qm ) { qmenu_free ( qm ) ; }
int qmenu_id ( Qmenu const * qm ) { return qm - > id ; }
static ORCA_NOINLINE Qmenu_item * qmenu_allocitems ( Qmenu * qm , Usz count ) {
void qmenu_destroy ( Qmenu * qm )
{
qmenu_free ( qm ) ;
}
int qmenu_id ( Qmenu const * qm )
{
return qm - > id ;
}
static ORCA_NOINLINE Qmenu_item * qmenu_allocitems ( Qmenu * qm , Usz count )
{
Usz old_count = qm - > items_count ;
if ( old_count > SIZE_MAX - count ) // overflow
exit ( 1 ) ;
@ -402,7 +450,8 @@ static ORCA_NOINLINE Qmenu_item *qmenu_allocitems(Qmenu *qm, Usz count) {
qm - > items_count = new_count ;
return items + old_count ;
}
ORCA_NOINLINE static void qmenu_reprint ( Qmenu * qm ) {
ORCA_NOINLINE static void qmenu_reprint ( Qmenu * qm )
{
WINDOW * win = qm - > qblock . content_window ;
Qmenu_item * items = qm - > items ;
bool isfront = qm - > is_frontmost ;
@ -416,10 +465,12 @@ ORCA_NOINLINE static void qmenu_reprint(Qmenu *qm) {
waddstr ( win , items [ i ] . text ) ;
}
}
void qmenu_set_title ( Qmenu * qm , char const * title ) {
void qmenu_set_title ( Qmenu * qm , char const * title )
{
qblock_set_title ( & qm - > qblock , title ) ;
}
void qmenu_add_choice ( Qmenu * qm , int id , char const * text ) {
void qmenu_add_choice ( Qmenu * qm , int id , char const * text )
{
assert ( id ! = 0 ) ;
Qmenu_item * item = qmenu_allocitems ( qm , 1 ) ;
item - > text = text ;
@ -429,7 +480,8 @@ void qmenu_add_choice(Qmenu *qm, int id, char const *text) {
if ( ! qm - > current_item )
qm - > current_item = id ;
}
void qmenu_add_printf ( Qmenu * qm , int id , char const * fmt , . . . ) {
void qmenu_add_printf ( Qmenu * qm , int id , char const * fmt , . . . )
{
va_list ap ;
va_start ( ap , fmt ) ;
int textsize = vsnprintf ( NULL , 0 , fmt , ap ) ;
@ -450,21 +502,27 @@ void qmenu_add_printf(Qmenu *qm, int id, char const *fmt, ...) {
if ( ! qm - > current_item )
qm - > current_item = id ;
}
void qmenu_add_spacer ( Qmenu * qm ) {
void qmenu_add_spacer ( Qmenu * qm )
{
Qmenu_item * item = qmenu_allocitems ( qm , 1 ) ;
item - > text = " " ;
item - > id = 0 ;
item - > owns_string = false ;
item - > is_spacer = true ;
}
void qmenu_set_current_item ( Qmenu * qm , int id ) {
void qmenu_set_current_item ( Qmenu * qm , int id )
{
if ( qm - > current_item = = id )
return ;
qm - > current_item = id ;
qm - > needs_reprint = 1 ;
}
int qmenu_current_item ( Qmenu * qm ) { return qm - > current_item ; }
void qmenu_push_to_nav ( Qmenu * qm ) {
int qmenu_current_item ( Qmenu * qm )
{
return qm - > current_item ;
}
void qmenu_push_to_nav ( Qmenu * qm )
{
// Probably a programming error if there are no items. Make the menu visible
// so the programmer knows something went wrong.
if ( qm - > items_count = = 0 )
@ -489,7 +547,8 @@ void qmenu_push_to_nav(Qmenu *qm) {
qnav_stack_push ( & qm - > qblock , menu_min_h , menu_min_w ) ;
}
static void qmenu_free ( Qmenu * qm ) {
static void qmenu_free ( Qmenu * qm )
{
Qmenu_item * items = qm - > items ;
for ( Usz i = 0 , n = qm - > items_count ; i < n ; + + i ) {
if ( items [ i ] . owns_string )
@ -499,7 +558,8 @@ static void qmenu_free(Qmenu *qm) {
free ( qm ) ;
}
ORCA_NOINLINE static void qmenu_drive_upordown ( Qmenu * qm , bool downwards ) {
ORCA_NOINLINE static void qmenu_drive_upordown ( Qmenu * qm , bool downwards )
{
Qmenu_item * items = qm - > items ;
Usz n = qm - > items_count ;
if ( n < = 1 )
@ -529,7 +589,8 @@ found:;
}
}
bool qmenu_drive ( Qmenu * qm , int key , Qmenu_action * out_action ) {
bool qmenu_drive ( Qmenu * qm , int key , Qmenu_action * out_action )
{
switch ( key ) {
case 27 : {
out_action - > any . type = Qmenu_action_type_canceled ;
@ -551,9 +612,13 @@ bool qmenu_drive(Qmenu *qm, int key, Qmenu_action *out_action) {
return false ;
}
Qmenu * qmenu_of ( Qblock * qb ) { return ORCA_CONTAINER_OF ( qb , Qmenu , qblock ) ; }
Qmenu * qmenu_of ( Qblock * qb )
{
return ORCA_CONTAINER_OF ( qb , Qmenu , qblock ) ;
}
bool qmenu_top_is_menu ( int id ) {
bool qmenu_top_is_menu ( int id )
{
Qblock * qb = qnav_top_block ( ) ;
if ( ! qb )
return false ;
@ -563,7 +628,8 @@ bool qmenu_top_is_menu(int id) {
return qm - > id = = id ;
}
Qform * qform_create ( int id ) {
Qform * qform_create ( int id )
{
Qform * qf = ( Qform * ) malloc ( sizeof ( Qform ) ) ;
qblock_init ( & qf - > qblock , Qblock_type_qform ) ;
qf - > ncurses_form = NULL ;
@ -572,7 +638,8 @@ Qform *qform_create(int id) {
qf - > id = id ;
return qf ;
}
static void qform_free ( Qform * qf ) {
static void qform_free ( Qform * qf )
{
curs_set ( 0 ) ;
unpost_form ( qf - > ncurses_form ) ;
free_form ( qf - > ncurses_form ) ;
@ -581,12 +648,20 @@ static void qform_free(Qform *qf) {
}
free ( qf ) ;
}
int qform_id ( Qform const * qf ) { return qf - > id ; }
Qform * qform_of ( Qblock * qb ) { return ORCA_CONTAINER_OF ( qb , Qform , qblock ) ; }
void qform_set_title ( Qform * qf , char const * title ) {
int qform_id ( Qform const * qf )
{
return qf - > id ;
}
Qform * qform_of ( Qblock * qb )
{
return ORCA_CONTAINER_OF ( qb , Qform , qblock ) ;
}
void qform_set_title ( Qform * qf , char const * title )
{
qblock_set_title ( & qf - > qblock , title ) ;
}
void qform_add_line_input ( Qform * qf , int id , char const * initial ) {
void qform_add_line_input ( Qform * qf , int id , char const * initial )
{
FIELD * f = new_field ( 1 , 30 , 0 , 0 , 0 , 0 ) ;
if ( initial )
set_field_buffer ( f , 0 , initial ) ;
@ -596,7 +671,8 @@ void qform_add_line_input(Qform *qf, int id, char const *initial) {
+ + qf - > fields_count ;
qf - > ncurses_fields [ qf - > fields_count ] = NULL ;
}
void qform_push_to_nav ( Qform * qf ) {
void qform_push_to_nav ( Qform * qf )
{
qf - > ncurses_form = new_form ( qf - > ncurses_fields ) ;
int form_min_h , form_min_w ;
scale_form ( qf - > ncurses_form , & form_min_h , & form_min_w ) ;
@ -608,13 +684,15 @@ void qform_push_to_nav(Qform *qf) {
curs_set ( 1 ) ;
form_driver ( qf - > ncurses_form , REQ_END_LINE ) ;
}
void qform_single_line_input ( int id , char const * title , char const * initial ) {
void qform_single_line_input ( int id , char const * title , char const * initial )
{
Qform * qf = qform_create ( id ) ;
qform_set_title ( qf , title ) ;
qform_add_line_input ( qf , 1 , initial ) ;
qform_push_to_nav ( qf ) ;
}
bool qform_drive ( Qform * qf , int key , Qform_action * out_action ) {
bool qform_drive ( Qform * qf , int key , Qform_action * out_action )
{
switch ( key ) {
case 27 :
out_action - > any . type = Qform_action_type_canceled ;
@ -653,7 +731,8 @@ bool qform_drive(Qform *qf, int key, Qform_action *out_action) {
form_driver ( qf - > ncurses_form , key ) ;
return false ;
}
static Usz size_without_trailing_spaces ( char const * str ) {
static Usz size_without_trailing_spaces ( char const * str )
{
Usz size = strlen ( str ) ;
for ( ; ; ) {
if ( size = = 0 )
@ -664,7 +743,8 @@ static Usz size_without_trailing_spaces(char const *str) {
}
return size ;
}
static FIELD * qform_find_field ( Qform const * qf , int id ) {
static FIELD * qform_find_field ( Qform const * qf , int id )
{
Usz count = qf - > fields_count ;
for ( Usz i = 0 ; i < count ; + + i ) {
FIELD * f = qf - > ncurses_fields [ i ] ;
@ -673,7 +753,8 @@ static FIELD *qform_find_field(Qform const *qf, int id) {
}
return NULL ;
}
bool qform_get_text_line ( Qform const * qf , int id , oso * * out ) {
bool qform_get_text_line ( Qform const * qf , int id , oso * * out )
{
FIELD * f = qform_find_field ( qf , id ) ;
if ( ! f )
return false ;
@ -685,10 +766,12 @@ bool qform_get_text_line(Qform const *qf, int id, oso **out) {
osoputlen ( out , buf , trimmed ) ;
return true ;
}
bool qform_get_single_text_line ( Qform const * qf , struct oso * * out ) {
bool qform_get_single_text_line ( Qform const * qf , struct oso * * out )
{
return qform_get_text_line ( qf , 1 , out ) ;
}
oso * qform_get_nonempty_single_line_input ( Qform * qf ) {
oso * qform_get_nonempty_single_line_input ( Qform * qf )
{
oso * s = NULL ;
if ( qform_get_text_line ( qf , 1 , & s ) & & osolen ( s ) > 0 )
return s ;