2018-02-08 23:40:26 +00:00
// Hyperbolic Rogue -- dialogs
// Copyright (C) 2011-2018 Zeno Rogue, see 'hyper.cpp' for details
2019-08-10 11:43:24 +00:00
/** \file dialogs.cpp
* \ brief Implementation of various generic dialogs and elements of dialog windows
*/
2017-03-23 10:53:57 +00:00
2019-09-05 07:15:40 +00:00
# include "hyper.h"
2018-06-10 23:58:31 +00:00
namespace hr {
2019-08-10 00:16:48 +00:00
EX const char * COLORBAR = " ### " ;
2017-10-29 11:46:57 +00:00
2019-08-09 19:00:52 +00:00
EX namespace dialog {
# if HDR
2019-08-10 08:57:14 +00:00
# define IFM(x) (mousing?"":x)
2019-08-09 19:00:52 +00:00
static const int DONT_SHOW = 16 ;
enum tDialogItem { diTitle , diItem , diBreak , diHelp , diInfo , diIntSlider , diSlider , diBigItem , diKeyboard } ;
struct item {
tDialogItem type ;
string body ;
string value ;
int key ;
color_t color , colorv , colork , colors , colorc ;
int scale ;
double param ;
int p1 , p2 , p3 ;
int position ;
} ;
struct scaler {
ld ( * direct ) ( ld ) ;
ld ( * inverse ) ( ld ) ;
bool positive ;
} ;
2020-02-23 01:51:27 +00:00
static inline ld identity_f ( ld x ) { return x ; }
2019-08-09 19:00:52 +00:00
const static scaler identity = { identity_f , identity_f , false } ;
const static scaler logarithmic = { log , exp , true } ;
const static scaler asinhic = { asinh , sinh , false } ;
const static scaler asinhic100 = { [ ] ( ld x ) { return asinh ( x * 100 ) ; } , [ ] ( ld x ) { return sinh ( x ) / 100 ; } , false } ;
struct numberEditor {
ld * editwhat ;
string s ;
ld vmin , vmax , step , dft ;
string title , help ;
scaler sc ;
int * intval ; ld intbuf ;
bool animatable ;
} ;
2017-03-23 10:53:57 +00:00
2019-08-09 19:00:52 +00:00
extern numberEditor ne ;
2018-09-05 13:18:40 +00:00
2019-08-09 19:00:52 +00:00
inline void scaleLog ( ) { ne . sc = logarithmic ; }
inline void scaleSinh ( ) { ne . sc = asinhic ; }
inline void scaleSinh100 ( ) { ne . sc = asinhic100 ; }
# endif
EX color_t dialogcolor = 0xC0C0C0 ;
EX void addBack ( ) {
2018-06-12 22:00:01 +00:00
addItem ( XLAT ( " go back " ) , SDLK_ESCAPE ) ;
}
2019-08-09 19:00:52 +00:00
EX void addHelp ( ) {
2018-06-12 22:00:01 +00:00
addItem ( XLAT ( " help " ) , SDLK_F1 ) ;
}
2019-08-09 19:00:52 +00:00
EX namespace zoom {
2017-03-23 10:53:57 +00:00
int zoomf = 1 , shiftx , shifty ;
bool zoomoff = false ;
2018-07-22 10:54:05 +00:00
void nozoom ( ) {
zoomf = 1 ; shiftx = 0 ; shifty = 0 ; zoomoff = false ;
2017-03-23 10:53:57 +00:00
}
2018-07-22 10:54:05 +00:00
void initzoom ( ) {
2017-03-23 10:53:57 +00:00
zoomf = 3 ;
shiftx = - 2 * mousex ;
2018-07-22 10:54:05 +00:00
if ( mousex < vid . xres / 6 ) shiftx = 0 ;
if ( mousex > vid . xres * 5 / 6 ) shiftx = - 2 * vid . xres ;
2017-03-23 10:53:57 +00:00
shifty = - 2 * mousey ;
2018-07-22 10:54:05 +00:00
if ( mousey < vid . yres / 6 ) shifty = 0 ;
if ( mousey > vid . yres * 5 / 6 ) shifty = - 2 * vid . yres ;
}
void stopzoom ( ) { zoomoff = true ; }
2019-08-09 19:00:52 +00:00
EX bool displayfr ( int x , int y , int b , int size , const string & s , color_t color , int align ) {
2018-07-22 10:54:05 +00:00
return hr : : displayfr ( x * zoomf + shiftx , y * zoomf + shifty , b , size * zoomf , s , color , align ) ;
}
2019-08-09 19:00:52 +00:00
EX bool displayfr_highlight ( int x , int y , int b , int size , const string & s , color_t color , int align , int hicolor IS ( 0xFFFF00 ) ) {
2018-07-22 10:54:05 +00:00
bool clicked = hr : : displayfr ( x * zoomf + shiftx , y * zoomf + shifty , b , size * zoomf , s , color , align ) ;
if ( clicked ) hr : : displayfr ( x * zoomf + shiftx , y * zoomf + shifty , b , size * zoomf , s , hicolor , align ) ;
return clicked ;
2017-03-23 10:53:57 +00:00
}
2020-02-23 01:51:27 +00:00
EX }
2018-07-22 10:54:05 +00:00
# if CAP_MENUSCALING && CAP_SDL
2019-08-09 19:00:52 +00:00
EX void handleZooming ( SDL_Event & ev ) {
2018-07-22 10:54:05 +00:00
using namespace zoom ;
if ( zoomoff | | ! ( cmode & sm : : ZOOMABLE ) ) {
nozoom ( ) ; return ;
2017-03-23 10:53:57 +00:00
}
2018-07-22 10:54:05 +00:00
if ( ev . type = = SDL_MOUSEBUTTONDOWN ) initzoom ( ) ;
if ( ev . type = = SDL_MOUSEBUTTONUP & & zoomf > 1 ) stopzoom ( ) ;
2017-03-23 10:53:57 +00:00
}
2019-09-06 06:17:02 +00:00
# endif
# if !(CAP_MENUSCALING && CAP_SDL)
2019-08-09 19:00:52 +00:00
EX void handleZooming ( SDL_Event & ev ) { }
2017-03-23 10:53:57 +00:00
# endif
2019-08-09 19:00:52 +00:00
EX vector < item > items ;
2017-03-23 10:53:57 +00:00
2019-08-09 19:00:52 +00:00
EX item & lastItem ( ) { return items [ items . size ( ) - 1 ] ; }
2019-11-24 22:40:26 +00:00
EX item & titleItem ( ) { return items [ 0 ] ; }
2017-03-23 10:53:57 +00:00
2019-08-09 19:00:52 +00:00
EX map < int , reaction_t > key_actions ;
2018-07-09 16:11:49 +00:00
2019-08-09 19:00:52 +00:00
EX void add_key_action ( int key , const reaction_t & action ) {
2018-07-09 16:11:49 +00:00
key_actions [ key ] = action ;
}
2019-08-14 07:14:52 +00:00
EX void add_key_action_adjust ( int & key , const reaction_t & action ) {
while ( key_actions . count ( key ) ) key + + ;
add_key_action ( key , action ) ;
}
2019-08-09 19:00:52 +00:00
EX void add_action ( const reaction_t & action ) {
2019-08-14 07:14:52 +00:00
add_key_action_adjust ( lastItem ( ) . key , action ) ;
2018-12-14 12:26:17 +00:00
}
2018-07-09 16:11:49 +00:00
2019-08-09 19:00:52 +00:00
EX void add_action_push ( const reaction_t & action ) { add_action ( [ action ] { pushScreen ( action ) ; } ) ; }
2019-03-30 22:45:28 +00:00
2019-08-09 19:00:52 +00:00
EX void handler ( int sym , int uni ) {
2018-07-09 16:11:49 +00:00
dialog : : handleNavigation ( sym , uni ) ;
if ( doexiton ( sym , uni ) ) popScreen ( ) ;
2020-02-23 01:51:27 +00:00
}
2018-07-09 16:11:49 +00:00
2019-08-09 19:00:52 +00:00
EX void init ( ) {
2018-07-09 16:11:49 +00:00
items . clear ( ) ;
key_actions . clear ( ) ;
keyhandler = dialog : : handler ;
}
2017-03-23 10:53:57 +00:00
2019-08-09 19:00:52 +00:00
EX string keyname ( int k ) {
2017-03-23 10:53:57 +00:00
if ( k = = 0 ) return " " ;
if ( k = = SDLK_ESCAPE ) return " Esc " ;
if ( k = = SDLK_F5 ) return " F5 " ;
if ( k = = SDLK_F10 ) return " F10 " ;
2018-04-10 03:19:49 +00:00
if ( k = = SDLK_F1 ) return " F1 " ;
2017-03-23 10:53:57 +00:00
if ( k = = SDLK_HOME ) return " Home " ;
2017-04-08 15:18:29 +00:00
if ( k = = SDLK_BACKSPACE ) return " Backspace " ;
if ( k = = SDLK_RETURN ) return " Enter " ;
2017-03-23 10:53:57 +00:00
if ( k = = 32 ) return " space " ;
if ( k > = 1 & & k < = 26 ) { string s = " Ctrl+ " ; s + = ( k + 64 ) ; return s ; }
if ( k < 128 ) { string s ; s + = k ; return s ; }
return " ? " ;
}
2019-08-09 19:00:52 +00:00
EX void addSlider ( double d1 , double d2 , double d3 , int key ) {
2017-03-23 10:53:57 +00:00
item it ;
it . type = diSlider ;
2018-09-05 13:18:40 +00:00
it . color = dialogcolor ;
2017-03-23 10:53:57 +00:00
it . scale = 100 ;
it . key = key ;
it . param = ( d2 - d1 ) / ( d3 - d1 ) ;
items . push_back ( it ) ;
}
2019-08-09 19:00:52 +00:00
EX void addIntSlider ( int d1 , int d2 , int d3 , int key ) {
2019-05-21 22:03:51 +00:00
item it ;
it . type = diIntSlider ;
it . color = dialogcolor ;
it . scale = 100 ;
it . key = key ;
it . p1 = ( d2 - d1 ) ;
it . p2 = ( d3 - d1 ) ;
items . push_back ( it ) ;
}
2019-08-09 19:00:52 +00:00
EX void addSelItem ( string body , string value , int key ) {
2017-03-23 10:53:57 +00:00
item it ;
it . type = diItem ;
it . body = body ;
it . value = value ;
it . key = key ;
2018-09-05 13:18:40 +00:00
it . color = dialogcolor ;
2017-03-23 10:53:57 +00:00
it . colork = 0x808080 ;
it . colorv = 0x80A040 ;
it . colorc = 0xFFD500 ;
it . colors = 0xFF8000 ;
if ( value = = ONOFF ( true ) ) it . colorv = 0x40FF40 ;
if ( value = = ONOFF ( false ) ) it . colorv = 0xC04040 ;
it . scale = 100 ;
items . push_back ( it ) ;
}
2019-08-09 19:00:52 +00:00
EX void addBoolItem ( string body , bool value , int key ) {
2017-03-23 10:53:57 +00:00
addSelItem ( body , ONOFF ( value ) , key ) ;
}
2017-12-21 23:51:55 +00:00
2019-08-09 21:53:20 +00:00
EX int displaycolor ( color_t col ) {
2017-12-21 23:51:55 +00:00
int c = col > > 8 ;
if ( ! c ) return 0x181818 ;
return c ;
}
2017-03-23 10:53:57 +00:00
2019-08-09 19:00:52 +00:00
EX void addKeyboardItem ( string keys ) {
2019-07-03 05:31:09 +00:00
item it ;
it . type = diKeyboard ;
it . body = keys ;
it . color = dialogcolor ;
it . colors = 0xFF8000 ;
2019-07-03 06:06:42 +00:00
it . scale = 150 ;
2019-07-03 05:31:09 +00:00
items . push_back ( it ) ;
}
2019-08-09 19:00:52 +00:00
EX void addColorItem ( string body , int value , int key ) {
2017-03-23 10:53:57 +00:00
item it ;
it . type = diItem ;
it . body = body ;
it . value = COLORBAR ;
it . key = key ;
2017-12-21 23:51:55 +00:00
it . color = it . colorv = displaycolor ( value ) ;
2017-03-23 10:53:57 +00:00
it . colors = it . color ^ 0x404040 ;
it . colorc = it . color ^ 0x808080 ;
2017-05-27 19:40:40 +00:00
it . colork = 0x808080 ;
2017-03-23 10:53:57 +00:00
it . scale = 100 ;
items . push_back ( it ) ;
}
2019-08-09 19:00:52 +00:00
EX void addHelp ( string body ) {
2017-03-23 10:53:57 +00:00
item it ;
it . type = diHelp ;
it . body = body ;
it . scale = 100 ;
2018-06-22 12:47:24 +00:00
if ( isize ( body ) > = 500 ) it . scale = 70 ;
2017-03-23 10:53:57 +00:00
items . push_back ( it ) ;
}
2019-08-09 19:00:52 +00:00
EX void addInfo ( string body , color_t color IS ( dialogcolor ) ) {
2017-03-23 10:53:57 +00:00
item it ;
it . type = diInfo ;
it . body = body ;
it . color = color ;
it . scale = 100 ;
items . push_back ( it ) ;
}
2019-08-09 19:00:52 +00:00
EX void addItem ( string body , int key ) {
2017-03-23 10:53:57 +00:00
item it ;
it . type = diItem ;
it . body = body ;
it . key = key ;
2018-09-05 13:18:40 +00:00
it . color = dialogcolor ;
2017-03-23 10:53:57 +00:00
it . colork = 0x808080 ;
it . colors = 0xFFD500 ;
it . colorc = 0xFF8000 ;
it . scale = 100 ;
items . push_back ( it ) ;
}
2019-08-09 19:00:52 +00:00
EX void addBigItem ( string body , int key ) {
2017-08-06 12:50:16 +00:00
item it ;
it . type = diBigItem ;
it . body = body ;
it . key = key ;
it . color = 0xC06000 ;
it . colors = 0xFFD500 ;
it . colorc = 0xFF8000 ;
it . scale = 150 ;
items . push_back ( it ) ;
}
2019-08-09 19:00:52 +00:00
EX int addBreak ( int val ) {
2017-03-23 10:53:57 +00:00
item it ;
it . type = diBreak ;
it . scale = val ;
items . push_back ( it ) ;
2017-07-04 13:38:33 +00:00
return items . size ( ) - 1 ;
2017-03-23 10:53:57 +00:00
}
2019-08-09 19:00:52 +00:00
EX void addTitle ( string body , color_t color , int scale ) {
2017-03-23 10:53:57 +00:00
item it ;
it . type = diTitle ;
it . body = body ;
it . color = color ;
it . scale = scale ;
items . push_back ( it ) ;
}
2019-08-09 19:00:52 +00:00
EX void init ( string title , color_t color IS ( 0xE8E8E8 ) , int scale IS ( 150 ) , int brk IS ( 60 ) ) {
2017-03-23 10:53:57 +00:00
init ( ) ;
addTitle ( title , color , scale ) ;
addBreak ( brk ) ;
}
2019-08-09 19:00:52 +00:00
EX int dcenter , dwidth ;
2017-04-04 09:13:15 +00:00
2019-08-09 19:00:52 +00:00
EX int dialogflags ;
2017-04-04 09:13:15 +00:00
2019-08-09 19:00:52 +00:00
EX int displayLong ( string str , int siz , int y , bool measure ) {
2017-03-23 10:53:57 +00:00
int last = 0 ;
int lastspace = 0 ;
2017-04-04 09:13:15 +00:00
int xs , xo ;
2018-11-17 18:24:02 +00:00
if ( current_display - > sidescreen )
2017-04-08 15:18:29 +00:00
xs = dwidth - vid . fsize * 2 , xo = vid . yres + vid . fsize ;
2017-04-04 09:13:15 +00:00
else
xs = vid . xres * 618 / 1000 , xo = vid . xres * 186 / 1000 ;
2017-03-23 10:53:57 +00:00
2018-06-22 12:47:24 +00:00
for ( int i = 0 ; i < = isize ( str ) ; i + + ) {
2017-03-23 10:53:57 +00:00
int ls = 0 ;
int prev = last ;
if ( str [ i ] = = ' ' ) lastspace = i ;
if ( textwidth ( siz , str . substr ( last , i - last ) ) > xs ) {
if ( lastspace = = last ) ls = i - 1 , last = i - 1 ;
else ls = lastspace , last = ls + 1 ;
}
2018-06-22 12:47:24 +00:00
if ( str [ i ] = = 10 | | i = = isize ( str ) ) ls = i , last = i + 1 ;
2017-03-23 10:53:57 +00:00
if ( ls ) {
2018-09-05 13:18:40 +00:00
if ( ! measure ) displayfr ( xo , y , 2 , siz , str . substr ( prev , ls - prev ) , dialogcolor , 0 ) ;
2017-03-23 10:53:57 +00:00
if ( ls = = prev ) y + = siz / 2 ;
else y + = siz ;
lastspace = last ;
}
}
y + = siz / 2 ;
return y ;
}
2017-04-04 09:13:15 +00:00
2019-08-09 19:00:52 +00:00
EX int tothei , dialogwidth , dfsize , dfspace , leftwidth , rightwidth , innerwidth , itemx , keyx , valuex ;
2017-03-23 10:53:57 +00:00
2019-09-06 06:17:02 +00:00
EX string highlight_text ;
2017-03-23 10:53:57 +00:00
2019-08-09 19:00:52 +00:00
EX void measure ( ) {
2017-03-23 10:53:57 +00:00
tothei = 0 ;
dialogwidth = 0 ;
innerwidth = 0 ;
int N = items . size ( ) ;
for ( int i = 0 ; i < N ; i + + ) {
if ( items [ i ] . type = = diHelp )
tothei + = displayLong ( items [ i ] . body , dfsize * items [ i ] . scale / 100 , 0 , true ) ;
else {
tothei + = dfspace * items [ i ] . scale / 100 ;
if ( items [ i ] . type = = diItem )
innerwidth = max ( innerwidth , textwidth ( dfsize * items [ i ] . scale / 100 , items [ i ] . body ) ) ;
2017-08-06 12:50:16 +00:00
if ( items [ i ] . type = = diTitle | | items [ i ] . type = = diInfo | | items [ i ] . type = = diBigItem )
2017-03-23 10:53:57 +00:00
dialogwidth = max ( dialogwidth , textwidth ( dfsize * items [ i ] . scale / 100 , items [ i ] . body ) * 10 / 9 ) ;
}
}
leftwidth = ISMOBILE ? 0 : textwidth ( dfsize , " MMMMM " ) + dfsize / 2 ;
rightwidth = textwidth ( dfsize , " MMMMMMMM " ) + dfsize / 2 ;
int fwidth = innerwidth + leftwidth + rightwidth ;
dialogwidth = max ( dialogwidth , fwidth ) ;
2017-04-04 09:13:15 +00:00
itemx = dcenter - fwidth / 2 + leftwidth ;
keyx = dcenter - fwidth / 2 + leftwidth - dfsize / 2 ;
valuex = dcenter - fwidth / 2 + leftwidth + innerwidth + dfsize / 2 ;
2017-03-23 10:53:57 +00:00
}
2019-09-08 08:08:05 +00:00
EX purehookset hooks_display_dialog ;
2019-08-09 19:00:52 +00:00
EX void display ( ) {
2019-09-08 08:08:05 +00:00
callhooks ( hooks_display_dialog ) ;
2017-03-23 10:53:57 +00:00
int N = items . size ( ) ;
dfsize = vid . fsize ;
2017-07-22 23:33:27 +00:00
# if ISMOBILE || ISPANDORA
2017-03-23 10:53:57 +00:00
dfsize * = 3 ;
# endif
dfspace = dfsize * 5 / 4 ;
2017-04-04 09:13:15 +00:00
dcenter = vid . xres / 2 ;
dwidth = vid . xres ;
2017-07-12 17:50:39 +00:00
2018-11-17 18:24:02 +00:00
if ( current_display - > sidescreen ) {
2017-04-04 09:13:15 +00:00
dwidth = vid . xres - vid . yres ;
2017-04-08 15:18:29 +00:00
dcenter = vid . xres - dwidth / 2 ;
2017-04-04 09:13:15 +00:00
}
2017-07-12 17:50:39 +00:00
measure ( ) ;
2017-03-23 10:53:57 +00:00
while ( tothei > vid . yres - 5 * vid . fsize ) {
int adfsize = int ( dfsize * sqrt ( ( vid . yres - 5. * vid . fsize ) / tothei ) ) ;
if ( adfsize < dfsize - 1 ) dfsize = adfsize + 1 ;
else dfsize - - ;
dfspace = dfsize * 5 / 4 ;
measure ( ) ;
}
2017-04-04 09:13:15 +00:00
while ( dialogwidth > dwidth ) {
2017-03-23 10:53:57 +00:00
int adfsize = int ( dfsize * sqrt ( vid . xres * 1. / dialogwidth ) ) ;
if ( adfsize < dfsize - 1 ) dfsize = adfsize + 1 ;
else dfsize - - ; // keep dfspace
measure ( ) ;
}
tothei = ( vid . yres - tothei ) / 2 ;
for ( int i = 0 ; i < N ; i + + ) {
item & I = items [ i ] ;
if ( I . type = = diHelp ) {
tothei = displayLong ( items [ i ] . body , dfsize * items [ i ] . scale / 100 , tothei , false ) ;
continue ;
}
int top = tothei ;
tothei + = dfspace * I . scale / 100 ;
int mid = ( top + tothei ) / 2 ;
2017-07-04 13:38:33 +00:00
I . position = mid ;
2017-03-23 10:53:57 +00:00
if ( I . type = = diTitle | | I . type = = diInfo ) {
2019-11-24 22:40:26 +00:00
bool xthis = ( mousey > = top & & mousey < tothei & & I . key ) ;
2017-04-04 09:13:15 +00:00
displayfr ( dcenter , mid , 2 , dfsize * I . scale / 100 , I . body , I . color , 8 ) ;
2019-11-24 22:40:26 +00:00
if ( xthis ) getcstat = I . key ;
2017-03-23 10:53:57 +00:00
}
2017-08-06 12:50:16 +00:00
else if ( I . type = = diItem | | I . type = = diBigItem ) {
2017-03-23 10:53:57 +00:00
bool xthis = ( mousey > = top & & mousey < tothei ) ;
2018-01-05 16:29:26 +00:00
if ( cmode & sm : : DIALOG_STRICT_X )
2017-12-14 01:51:31 +00:00
xthis = xthis & & ( mousex > = dcenter - dialogwidth / 2 & & mousex < = dcenter + dialogwidth / 2 ) ;
2017-07-22 23:33:27 +00:00
# if ISMOBILE
2017-03-23 10:53:57 +00:00
if ( xthis & & mousepressed )
I . color = I . colorc ;
# else
if ( xthis & & mousemoved ) {
highlight_text = I . body ;
mousemoved = false ;
}
if ( highlight_text = = I . body ) {
I . color = ( xthis & & mousepressed & & actonrelease ) ? I . colorc : I . colors ;
}
# endif
2017-08-06 12:50:16 +00:00
if ( I . type = = diBigItem ) {
displayfr ( dcenter , mid , 2 , dfsize * I . scale / 100 , I . body , I . color , 8 ) ;
}
else {
if ( ! mousing )
2019-08-14 07:14:52 +00:00
displayfr ( keyx , mid , 2 , dfsize * I . scale / 100 , keyname ( I . key ) , I . colork , 16 ) ;
2017-08-06 12:50:16 +00:00
displayfr ( itemx , mid , 2 , dfsize * I . scale / 100 , I . body , I . color , 0 ) ;
2019-11-30 18:28:55 +00:00
int siz = dfsize * I . scale / 100 ;
while ( siz > 6 & & textwidth ( siz , I . value ) > = vid . xres - valuex ) siz - - ;
displayfr ( valuex , mid , 2 , siz , I . value , I . colorv , 0 ) ;
2017-08-06 12:50:16 +00:00
}
2017-03-23 10:53:57 +00:00
if ( xthis ) getcstat = I . key ;
}
2019-05-21 22:03:51 +00:00
else if ( among ( I . type , diSlider , diIntSlider ) ) {
2017-03-23 10:53:57 +00:00
bool xthis = ( mousey > = top & & mousey < tothei ) ;
2017-04-04 09:13:15 +00:00
int sl , sr ;
2018-11-17 18:24:02 +00:00
if ( current_display - > sidescreen )
2017-04-04 09:13:15 +00:00
sl = vid . yres + vid . fsize * 2 , sr = vid . xres - vid . fsize * 2 ;
else
sl = vid . xres / 4 , sr = vid . xres * 3 / 4 ;
int sw = sr - sl ;
2019-05-21 22:03:51 +00:00
if ( I . type = = diSlider ) {
displayfr ( sl , mid , 2 , dfsize * I . scale / 100 , " ( " , I . color , 16 ) ;
displayfr ( sl + double ( sw * I . param ) , mid , 2 , dfsize * I . scale / 100 , " # " , I . color , 8 ) ;
displayfr ( sr , mid , 2 , dfsize * I . scale / 100 , " ) " , I . color , 0 ) ;
}
else {
displayfr ( sl , mid , 2 , dfsize * I . scale / 100 , " { " , I . color , 16 ) ;
if ( I . p2 < sw / 4 ) for ( int a = 0 ; a < = I . p2 ; a + + ) if ( a ! = I . p1 )
displayfr ( sl + double ( sw * a / I . p2 ) , mid , 2 , dfsize * I . scale / 100 , a = = I . p1 ? " # " : " . " , I . color , 8 ) ;
displayfr ( sl + double ( sw * I . p1 / I . p2 ) , mid , 2 , dfsize * I . scale / 100 , " # " , I . color , 8 ) ;
displayfr ( sr , mid , 2 , dfsize * I . scale / 100 , " } " , I . color , 0 ) ;
}
2017-03-23 10:53:57 +00:00
if ( xthis ) getcstat = I . key , inslider = true ;
}
2019-07-03 05:31:09 +00:00
else if ( I . type = = diKeyboard ) {
int len = 0 ;
for ( char c : I . body )
if ( c = = ' ' | | c = = ' \t ' ) len + = 3 ;
else len + + ;
int sl , sr ;
if ( current_display - > sidescreen )
sl = vid . yres + vid . fsize * 2 , sr = vid . xres - vid . fsize * 2 ;
else
sl = vid . xres / 4 , sr = vid . xres * 3 / 4 ;
int pos = 0 ;
for ( char c : I . body ) {
string s = " " ;
s + = c ;
int nlen = 1 ;
if ( c = = ' ' ) s = " SPACE " , nlen = 3 ;
if ( c = = ' \b ' ) s = " ⌫ " , nlen = 1 ;
if ( c = = ' \r ' | | c = = ' \n ' ) s = " ⏎ " , nlen = 1 ;
if ( c = = 1 ) s = " ← " , nlen = 1 ;
if ( c = = 2 ) s = " → " , nlen = 1 ;
if ( c = = 3 ) s = " π " ;
if ( c = = ' \t ' ) s = " CLEAR " , nlen = 3 ;
2019-07-12 21:10:57 +00:00
int left = sl + ( sr - sl ) * pos / len ;
pos + = nlen ;
int right = sl + ( sr - sl ) * pos / len ;
bool in = ( mousex > = left & & mousex < = right & & mousey > = top & & mousey < tothei ) ;
int xpos = ( left + right ) / 2 ;
if ( in ) {
2019-07-03 05:31:09 +00:00
if ( c = = 1 ) getcstat = SDLK_LEFT ;
else if ( c = = 2 ) getcstat = SDLK_RIGHT ;
else getcstat = c ;
}
2019-07-12 21:10:57 +00:00
displayfr ( xpos , mid , 2 , dfsize * I . scale / 100 , s , in ? I . colors : I . color , 8 ) ;
2019-07-03 05:31:09 +00:00
}
}
2017-03-23 10:53:57 +00:00
}
}
2017-08-06 12:50:16 +00:00
bool isitem ( item & it ) {
return it . type = = diItem | | it . type = = diBigItem ;
}
2017-03-23 10:53:57 +00:00
2019-08-09 19:00:52 +00:00
EX void handleNavigation ( int & sym , int & uni ) {
2018-12-06 11:31:51 +00:00
if ( uni = = ' \n ' | | uni = = ' \r ' | | DIRECTIONKEY = = SDLK_KP5 )
2018-06-22 12:47:24 +00:00
for ( int i = 0 ; i < isize ( items ) ; i + + )
2017-08-06 12:50:16 +00:00
if ( isitem ( items [ i ] ) )
2017-03-23 10:53:57 +00:00
if ( items [ i ] . body = = highlight_text ) {
uni = sym = items [ i ] . key ;
return ;
}
2018-12-06 11:31:51 +00:00
if ( DKEY = = SDLK_PAGEDOWN ) {
2018-06-22 12:47:24 +00:00
for ( int i = 0 ; i < isize ( items ) ; i + + )
2017-08-06 12:50:16 +00:00
if ( isitem ( items [ i ] ) )
2017-03-23 10:53:57 +00:00
highlight_text = items [ i ] . body ;
}
2018-12-06 11:31:51 +00:00
if ( DKEY = = SDLK_PAGEUP ) {
2018-06-22 12:47:24 +00:00
for ( int i = 0 ; i < isize ( items ) ; i + + )
2017-08-06 12:50:16 +00:00
if ( isitem ( items [ i ] ) ) {
2017-03-23 10:53:57 +00:00
highlight_text = items [ i ] . body ;
break ;
}
}
2018-12-06 11:31:51 +00:00
if ( DKEY = = SDLK_UP ) {
2017-03-23 10:53:57 +00:00
string last = " " ;
2018-06-22 12:47:24 +00:00
for ( int i = 0 ; i < isize ( items ) ; i + + )
2017-08-06 12:50:16 +00:00
if ( isitem ( items [ i ] ) )
2017-03-23 10:53:57 +00:00
last = items [ i ] . body ;
uni = sym = 0 ;
2018-06-22 12:47:24 +00:00
for ( int i = 0 ; i < isize ( items ) ; i + + )
2017-08-06 12:50:16 +00:00
if ( isitem ( items [ i ] ) ) {
2017-03-23 10:53:57 +00:00
if ( items [ i ] . body = = highlight_text ) {
highlight_text = last ; return ;
}
else last = items [ i ] . body ;
}
highlight_text = last ;
}
2018-12-06 11:31:51 +00:00
if ( DKEY = = SDLK_DOWN ) {
2017-03-23 10:53:57 +00:00
int state = 0 ;
2018-06-22 12:47:24 +00:00
for ( int i = 0 ; i < isize ( items ) ; i + + )
2017-08-06 12:50:16 +00:00
if ( isitem ( items [ i ] ) ) {
2017-03-23 10:53:57 +00:00
if ( state ) { highlight_text = items [ i ] . body ; return ; }
else if ( items [ i ] . body = = highlight_text ) state = 1 ;
}
2018-06-22 12:47:24 +00:00
for ( int i = 0 ; i < isize ( items ) ; i + + )
2017-08-06 12:50:16 +00:00
if ( isitem ( items [ i ] ) )
2017-03-23 10:53:57 +00:00
highlight_text = items [ i ] . body ;
uni = sym = 0 ;
}
2018-09-10 14:42:35 +00:00
if ( key_actions . count ( uni ) ) {
key_actions [ uni ] ( ) ;
2018-07-09 16:11:49 +00:00
sym = uni = 0 ;
return ;
}
2018-09-10 14:42:35 +00:00
if ( key_actions . count ( sym ) ) {
key_actions [ sym ] ( ) ;
2018-07-09 16:11:49 +00:00
sym = uni = 0 ;
return ;
}
2017-03-23 10:53:57 +00:00
}
2018-09-04 17:53:42 +00:00
color_t colorhistory [ 10 ] = {
2017-03-23 10:53:57 +00:00
0x202020FF , 0x800000FF , 0x008000FF , 0x000080FF ,
0x404040FF , 0xC0C0C0FF , 0x804000FF , 0xC0C000FF ,
0x408040FF , 0xFFD500FF
} , lch ;
2019-08-09 19:00:52 +00:00
EX color_t * palette ;
2017-03-23 10:53:57 +00:00
2017-05-27 19:40:40 +00:00
int colorp = 0 ;
2018-09-04 17:53:42 +00:00
color_t * colorPointer ;
2017-07-10 18:47:38 +00:00
2019-08-09 19:00:52 +00:00
EX void handleKeyColor ( int sym , int uni ) {
2017-07-16 21:00:55 +00:00
unsigned & color = * colorPointer ;
2018-11-08 20:56:06 +00:00
int shift = colorAlpha ? 0 : 8 ;
2017-07-10 18:47:38 +00:00
if ( uni > = ' A ' & & uni < = ' D ' ) {
2017-12-03 10:48:02 +00:00
int x = ( mousex - ( dcenter - dwidth / 4 ) ) * 510 / dwidth ;
2017-07-10 18:47:38 +00:00
if ( x < 0 ) x = 0 ;
if ( x > 255 ) x = 255 ;
2018-11-08 20:56:06 +00:00
part ( color , uni - ' A ' ) = x ;
2017-07-10 18:47:38 +00:00
}
2018-02-03 19:04:33 +00:00
else if ( uni = = ' ' | | uni = = ' \n ' | | uni = = ' \r ' ) {
2017-07-10 18:47:38 +00:00
bool inHistory = false ;
2018-11-08 20:56:06 +00:00
for ( int i = 0 ; i < 10 ; i + + ) if ( colorhistory [ i ] = = ( color < < shift ) )
2017-07-10 18:47:38 +00:00
inHistory = true ;
2018-11-08 20:56:06 +00:00
if ( ! inHistory ) { colorhistory [ lch ] = ( color < < shift ) ; lch + + ; lch % = 10 ; }
2017-07-10 18:47:38 +00:00
popScreen ( ) ;
2020-05-31 15:11:59 +00:00
if ( reaction ) reaction ( ) ;
if ( reaction_final ) reaction_final ( ) ;
2017-07-10 18:47:38 +00:00
}
else if ( uni > = ' 0 ' & & uni < = ' 9 ' ) {
2018-11-08 20:56:06 +00:00
color = colorhistory [ uni - ' 0 ' ] > > shift ;
2017-07-10 18:47:38 +00:00
}
else if ( palette & & uni > = ' a ' & & uni < ' a ' + ( int ) palette [ 0 ] ) {
2018-11-08 20:56:06 +00:00
color = palette [ 1 + uni - ' a ' ] > > shift ;
2017-07-10 18:47:38 +00:00
}
2018-12-06 11:31:51 +00:00
else if ( DKEY = = SDLK_DOWN ) {
2017-07-10 18:47:38 +00:00
colorp = ( colorp - 1 ) & 3 ;
}
2018-12-06 11:31:51 +00:00
else if ( DKEY = = SDLK_UP ) {
2017-07-10 18:47:38 +00:00
colorp = ( colorp + 1 ) & 3 ;
}
2018-12-06 11:31:51 +00:00
else if ( DKEY = = SDLK_LEFT ) {
2017-07-10 18:47:38 +00:00
unsigned char * pts = ( unsigned char * ) & color ;
pts [ colorp ] - = abs ( shiftmul ) < .6 ? 1 : 17 ;
}
2018-12-06 11:31:51 +00:00
else if ( DKEY = = SDLK_RIGHT ) {
2017-07-10 18:47:38 +00:00
unsigned char * pts = ( unsigned char * ) & color ;
pts [ colorp ] + = abs ( shiftmul ) < .6 ? 1 : 17 ;
}
2017-12-19 13:34:38 +00:00
else if ( doexiton ( sym , uni ) ) {
2017-07-10 18:47:38 +00:00
popScreen ( ) ;
2020-05-31 15:11:59 +00:00
if ( reaction_final ) reaction_final ( ) ;
2017-12-19 13:34:38 +00:00
}
2017-07-10 18:47:38 +00:00
}
2019-08-09 19:00:52 +00:00
EX bool colorAlpha ;
2018-11-08 20:56:06 +00:00
2019-08-09 19:00:52 +00:00
EX void drawColorDialog ( ) {
2017-12-03 10:48:02 +00:00
cmode = sm : : NUMBER | dialogflags ;
if ( cmode & sm : : SIDE ) gamescreen ( 0 ) ;
dcenter = vid . xres / 2 ;
dwidth = vid . xres ;
2018-11-17 18:24:02 +00:00
if ( current_display - > sidescreen ) {
2017-12-03 10:48:02 +00:00
dwidth = vid . xres - vid . yres ;
dcenter = vid . xres - dwidth / 2 ;
}
2018-09-04 17:53:42 +00:00
color_t color = * colorPointer ;
2017-07-10 18:47:38 +00:00
2017-03-23 10:53:57 +00:00
int ash = 8 ;
for ( int j = 0 ; j < 10 ; j + + ) {
2017-12-03 10:48:02 +00:00
int x = dcenter + vid . fsize * 2 * ( j - 5 ) ;
2017-03-23 10:53:57 +00:00
int y = vid . yres / 2 - 5 * vid . fsize ;
string s0 = " " ; s0 + = ( ' 0 ' + j ) ;
vid . fsize * = 2 ;
displayColorButton ( x , y , s0 , ' 0 ' + j , 0 , 0 , colorhistory [ j ] > > ash ) ;
vid . fsize / = 2 ;
}
if ( palette ) {
int q = palette [ 0 ] ;
for ( int i = 0 ; i < q ; i + + ) {
2017-12-03 10:48:02 +00:00
int x = dcenter + vid . fsize * ( 2 * i - q ) ;
2017-03-23 10:53:57 +00:00
int y = vid . yres / 2 - 7 * vid . fsize ;
string s0 = " " ; s0 + = ( ' a ' + i ) ;
vid . fsize * = 2 ;
displayColorButton ( x , y , s0 , ' a ' + i , 0 , 0 , palette [ i + 1 ] > > ash ) ;
vid . fsize / = 2 ;
}
}
for ( int i = 0 ; i < 4 ; i + + ) {
int y = vid . yres / 2 + ( 2 - i ) * vid . fsize * 2 ;
2018-11-08 20:56:06 +00:00
if ( i = = 3 & & ! colorAlpha ) continue ;
2017-05-27 19:40:40 +00:00
2018-10-23 18:09:54 +00:00
color_t col = ( ( i = = colorp ) & & ! mousing ) ? 0xFFD500 : dialogcolor ;
2017-03-23 10:53:57 +00:00
2017-12-03 10:48:02 +00:00
displayColorButton ( dcenter - dwidth / 4 , y , " ( " , 0 , 16 , 0 , col ) ;
2018-11-08 20:56:06 +00:00
string rgt = " ) " ; rgt + = " ABGR " [ i + ( colorAlpha ? 0 : 1 ) ] ;
2017-12-03 10:48:02 +00:00
displayColorButton ( dcenter + dwidth / 4 , y , rgt , 0 , 0 , 0 , col ) ;
2018-11-08 20:56:06 +00:00
displayColorButton ( dcenter - dwidth / 4 + dwidth * part ( color , i ) / 510 , y , " # " , 0 , 8 , 0 , col ) ;
2017-03-23 10:53:57 +00:00
if ( mousey > = y - vid . fsize & & mousey < y + vid . fsize )
getcstat = ' A ' + i , inslider = true ;
}
2018-11-08 20:56:06 +00:00
displayColorButton ( dcenter , vid . yres / 2 + vid . fsize * 6 , XLAT ( " select this color " ) + " : " + itsh ( color ) , ' ' , 8 , 0 , color > > ( colorAlpha ? ash : 0 ) ) ;
2018-08-01 01:59:20 +00:00
if ( extra_options ) extra_options ( ) ;
2017-07-10 18:47:38 +00:00
keyhandler = handleKeyColor ;
2017-03-23 10:53:57 +00:00
}
2019-08-09 19:00:52 +00:00
EX void openColorDialog ( unsigned int & col , unsigned int * pal IS ( palette ) ) {
2017-03-23 10:53:57 +00:00
colorPointer = & col ; palette = pal ;
2018-11-08 20:56:06 +00:00
colorAlpha = true ;
2017-12-03 10:48:02 +00:00
dialogflags = 0 ;
2017-07-10 18:47:38 +00:00
pushScreen ( drawColorDialog ) ;
2017-12-19 13:34:38 +00:00
reaction = reaction_t ( ) ;
2018-08-01 01:59:20 +00:00
extra_options = reaction_t ( ) ;
2017-03-23 10:53:57 +00:00
}
2019-08-09 19:00:52 +00:00
EX numberEditor ne ;
2017-03-23 10:53:57 +00:00
2019-08-09 19:00:52 +00:00
EX bool editingDetail ( ) {
2019-05-29 14:27:24 +00:00
return ne . editwhat = = & vid . highdetail | | ne . editwhat = = & vid . middetail ;
2017-03-23 10:53:57 +00:00
}
2017-11-06 20:18:12 +00:00
int ldtoint ( ld x ) {
if ( x > 0 ) return int ( x + .5 ) ;
else return int ( x - .5 ) ;
}
2019-08-09 19:00:52 +00:00
EX string disp ( ld x ) {
2019-09-27 16:03:10 +00:00
if ( dialogflags & sm : : HEXEDIT ) return " 0x " + itsh ( ( unsigned long long ) ( x ) ) ;
2019-01-18 20:04:29 +00:00
else if ( ne . intval ) return its ( ldtoint ( x ) ) ;
2019-05-21 22:01:30 +00:00
else return fts ( x ) ; }
2017-03-23 10:53:57 +00:00
2019-08-09 19:00:52 +00:00
EX reaction_t reaction ;
EX reaction_t reaction_final ;
2018-08-01 01:59:20 +00:00
2019-08-09 19:00:52 +00:00
EX reaction_t extra_options ;
2018-11-09 19:41:55 +00:00
2019-08-09 19:00:52 +00:00
EX void apply_slider ( ) {
2018-11-09 19:41:55 +00:00
if ( ne . intval ) * ne . intval = ldtoint ( * ne . editwhat ) ;
2017-09-01 20:06:36 +00:00
if ( reaction ) reaction ( ) ;
2018-11-09 19:41:55 +00:00
if ( ne . intval ) * ne . editwhat = * ne . intval ;
ne . s = disp ( * ne . editwhat ) ;
2019-02-17 17:43:39 +00:00
# if CAP_ANIMATIONS
2018-11-09 19:41:55 +00:00
anims : : deanimate ( * ne . editwhat ) ;
2019-02-17 17:43:39 +00:00
# endif
2018-11-09 19:41:55 +00:00
}
2019-08-09 19:00:52 +00:00
EX void use_hexeditor ( ) {
2019-01-18 20:04:29 +00:00
dialogflags | = sm : : HEXEDIT ;
ne . s = disp ( * ne . editwhat ) ;
}
2019-08-09 19:00:52 +00:00
EX void apply_edit ( ) {
2019-12-23 20:44:51 +00:00
try {
exp_parser ep ;
ep . s = ne . s ;
ld x = ep . rparse ( ) ;
if ( ne . sc . positive & & x < = 0 ) return ;
* ne . editwhat = x ;
if ( ne . intval ) * ne . intval = ldtoint ( * ne . editwhat ) ;
# if CAP_ANIMATIONS
if ( ne . animatable ) anims : : animate_parameter ( * ne . editwhat , ne . s , reaction ? reaction : reaction_final ) ;
# endif
if ( reaction ) reaction ( ) ;
}
catch ( hr_parse_exception & ) {
}
2018-11-09 19:41:55 +00:00
}
2017-03-23 10:53:57 +00:00
2019-08-09 19:00:52 +00:00
EX void bound_low ( ld val ) {
2018-11-09 19:41:55 +00:00
auto r = reaction ;
reaction = [ r , val ] ( ) {
if ( * ne . editwhat < val ) {
* ne . editwhat = val ;
if ( ne . intval ) * ne . intval = ldtoint ( * ne . editwhat ) ;
}
if ( r ) r ( ) ;
} ;
}
2017-03-23 10:53:57 +00:00
2019-08-09 19:00:52 +00:00
EX void bound_up ( ld val ) {
2018-11-09 19:41:55 +00:00
auto r = reaction ;
reaction = [ r , val ] ( ) {
if ( * ne . editwhat > val ) {
* ne . editwhat = val ;
if ( ne . intval ) * ne . intval = ldtoint ( * ne . editwhat ) ;
}
if ( r ) r ( ) ;
} ;
2017-03-23 10:53:57 +00:00
}
2018-11-09 19:41:55 +00:00
2019-08-09 19:00:52 +00:00
EX int numberdark ;
2018-05-15 21:26:27 +00:00
2019-08-09 19:00:52 +00:00
EX void formula_keyboard ( bool lr ) {
2019-07-03 05:31:09 +00:00
addKeyboardItem ( " 1234567890 " ) ;
2019-07-03 06:06:42 +00:00
addKeyboardItem ( " =+-*/^() \ x3 " ) ;
2019-07-03 05:31:09 +00:00
addKeyboardItem ( " qwertyuiop " ) ;
addKeyboardItem ( " asdfghjkl " ) ;
addKeyboardItem ( " zxcvbnm,. \b " ) ;
addKeyboardItem ( lr ? " \t \ x1 \ x2 " : " \t " ) ;
}
2019-08-09 19:00:52 +00:00
EX void drawNumberDialog ( ) {
2017-07-12 17:50:39 +00:00
cmode = sm : : NUMBER | dialogflags ;
2019-07-12 21:13:35 +00:00
if ( numberdark < DONT_SHOW )
2018-05-15 21:26:27 +00:00
gamescreen ( numberdark ) ;
2017-03-23 10:53:57 +00:00
init ( ne . title ) ;
addInfo ( ne . s ) ;
2019-05-21 22:03:51 +00:00
if ( ne . intval & & ne . sc . direct = = & identity_f )
addIntSlider ( int ( ne . vmin ) , int ( * ne . editwhat ) , int ( ne . vmax ) , 500 ) ;
else
addSlider ( ne . sc . direct ( ne . vmin ) , ne . sc . direct ( * ne . editwhat ) , ne . sc . direct ( ne . vmax ) , 500 ) ;
2017-03-23 10:53:57 +00:00
addBreak ( 100 ) ;
2020-05-03 18:56:41 +00:00
# if !ISMOBILE
2018-02-13 12:37:20 +00:00
addHelp ( XLAT ( " You can scroll with arrow keys -- Ctrl to fine-tune " ) ) ;
2017-03-23 10:53:57 +00:00
addBreak ( 100 ) ;
# endif
2018-06-12 22:00:01 +00:00
dialog : : addBack ( ) ;
2018-02-13 12:37:20 +00:00
addSelItem ( XLAT ( " default value " ) , disp ( ne . dft ) , SDLK_HOME ) ;
2017-03-23 10:53:57 +00:00
addBreak ( 100 ) ;
if ( ne . help ! = " " ) {
addHelp ( ne . help ) ;
2018-06-22 12:47:24 +00:00
// bool scal = !ISMOBILE && !ISPANDORA && isize(ne.help) > 160;
2017-10-08 22:21:39 +00:00
// if(scal) lastItem().scale = 30;
2017-03-23 10:53:57 +00:00
}
2018-08-01 01:59:20 +00:00
if ( extra_options ) extra_options ( ) ;
2018-04-23 10:34:14 +00:00
2019-07-03 06:06:42 +00:00
addBreak ( 100 ) ;
2019-07-03 05:31:09 +00:00
formula_keyboard ( false ) ;
2017-03-23 10:53:57 +00:00
display ( ) ;
2017-07-10 18:47:38 +00:00
keyhandler = [ ] ( int sym , int uni ) {
handleNavigation ( sym , uni ) ;
2019-07-03 05:31:09 +00:00
if ( ( uni > = ' 0 ' & & uni < = ' 9 ' ) | | among ( uni , ' . ' , ' + ' , ' - ' , ' * ' , ' / ' , ' ^ ' , ' ( ' , ' ) ' , ' , ' , 3 ) | | ( uni > = ' a ' & & uni < = ' z ' ) ) {
if ( uni = = 3 ) ne . s + = " pi " ;
else ne . s + = uni ;
2018-11-09 19:41:55 +00:00
apply_edit ( ) ;
2017-07-10 18:47:38 +00:00
}
else if ( uni = = ' \b ' | | uni = = ' \t ' ) {
2018-06-22 12:47:24 +00:00
ne . s = ne . s . substr ( 0 , isize ( ne . s ) - 1 ) ;
2017-07-10 18:47:38 +00:00
sscanf ( ne . s . c_str ( ) , LDF , ne . editwhat ) ;
2018-11-09 19:41:55 +00:00
apply_edit ( ) ;
2017-07-10 18:47:38 +00:00
}
2017-07-22 23:33:27 +00:00
# if !ISMOBILE
2018-12-06 11:31:51 +00:00
else if ( DKEY = = SDLK_RIGHT ) {
2017-07-10 18:47:38 +00:00
if ( ne . intval & & abs ( shiftmul ) < .6 )
( * ne . editwhat ) + + ;
else
2018-09-10 14:47:28 +00:00
* ne . editwhat = ne . sc . inverse ( ne . sc . direct ( * ne . editwhat ) + shiftmul * ne . step ) ;
2019-05-21 22:03:51 +00:00
if ( abs ( * ne . editwhat ) < ne . step * 1e-6 & & ! ne . intval ) * ne . editwhat = 0 ;
2018-11-09 19:41:55 +00:00
apply_slider ( ) ;
2017-07-10 18:47:38 +00:00
}
2018-12-06 11:31:51 +00:00
else if ( DKEY = = SDLK_LEFT ) {
2017-07-10 18:47:38 +00:00
if ( ne . intval & & abs ( shiftmul ) < .6 )
( * ne . editwhat ) - - ;
else
2018-09-10 14:47:28 +00:00
* ne . editwhat = ne . sc . inverse ( ne . sc . direct ( * ne . editwhat ) - shiftmul * ne . step ) ;
2019-05-21 22:03:51 +00:00
if ( abs ( * ne . editwhat ) < ne . step * 1e-6 & & ! ne . intval ) * ne . editwhat = 0 ;
2018-11-09 19:41:55 +00:00
apply_slider ( ) ;
2017-07-10 18:47:38 +00:00
}
# endif
else if ( sym = = SDLK_HOME ) {
* ne . editwhat = ne . dft ;
2018-11-09 19:41:55 +00:00
apply_slider ( ) ;
2017-07-10 18:47:38 +00:00
}
else if ( uni = = 500 ) {
int sl , sr ;
2018-11-17 18:24:02 +00:00
if ( current_display - > sidescreen )
2017-07-10 18:47:38 +00:00
sl = vid . yres + vid . fsize * 2 , sr = vid . xres - vid . fsize * 2 ;
else
sl = vid . xres / 4 , sr = vid . xres * 3 / 4 ;
ld d = ( mousex - sl + .0 ) / ( sr - sl ) ;
2019-05-21 22:03:51 +00:00
ld val = ne . sc . inverse ( d * ( ne . sc . direct ( ne . vmax ) - ne . sc . direct ( ne . vmin ) ) + ne . sc . direct ( ne . vmin ) ) ;
ld nextval = ne . sc . inverse ( ( mousex + 1. - sl ) / ( sr - sl ) * ( ne . sc . direct ( ne . vmax ) - ne . sc . direct ( ne . vmin ) ) + ne . sc . direct ( ne . vmin ) ) ;
ld dif = abs ( val - nextval ) ;
if ( dif > 1e-6 ) {
ld mul = 1 ;
while ( dif < 10 ) dif * = 10 , mul * = 10 ;
val * = mul ;
val = floor ( val + 0.5 ) ;
val / = mul ;
}
* ne . editwhat = val ;
2018-11-09 19:41:55 +00:00
apply_slider ( ) ;
2017-07-10 18:47:38 +00:00
}
2018-11-06 23:50:03 +00:00
else if ( doexiton ( sym , uni ) ) { popScreen ( ) ; if ( reaction_final ) reaction_final ( ) ; }
2017-07-10 18:47:38 +00:00
} ;
}
2017-03-23 10:53:57 +00:00
int nlpage = 1 ;
int wheelshift = 0 ;
2019-08-09 19:00:52 +00:00
EX int handlePage ( int & nl , int & nlm , int perpage ) {
2017-03-23 10:53:57 +00:00
nlm = nl ;
int onl = nl ;
int ret = 0 ;
if ( nlpage ) {
nl = nlm = perpage ;
if ( nlpage = = 2 ) ret = nlm ;
int w = wheelshift ;
int realw = 0 ;
while ( w < 0 & & ret ) {
ret - - ; w + + ; realw - - ;
}
while ( w > 0 & & ret + perpage < onl ) {
ret + + ; w - - ; realw + + ;
}
wheelshift = realw ;
if ( ret + nl > onl ) nl = onl - ret ;
}
return ret ;
}
2019-08-09 19:00:52 +00:00
EX void displayPageButtons ( int i , bool pages ) {
2017-03-23 10:53:57 +00:00
int i0 = vid . yres - vid . fsize ;
int xr = vid . xres / 80 ;
2018-09-05 13:18:40 +00:00
if ( pages ) if ( displayfrZH ( xr * 8 , i0 , 1 , vid . fsize , IFM ( " 1 - " ) + XLAT ( " page " ) + " 1 " , nlpage = = 1 ? 0xD8D8C0 : dialogcolor , 8 ) )
2017-03-23 10:53:57 +00:00
getcstat = ' 1 ' ;
2018-09-05 13:18:40 +00:00
if ( pages ) if ( displayfrZH ( xr * 24 , i0 , 1 , vid . fsize , IFM ( " 2 - " ) + XLAT ( " page " ) + " 2 " , nlpage = = 1 ? 0xD8D8C0 : dialogcolor , 8 ) )
2017-03-23 10:53:57 +00:00
getcstat = ' 2 ' ;
2018-09-05 13:18:40 +00:00
if ( pages ) if ( displayfrZH ( xr * 40 , i0 , 1 , vid . fsize , IFM ( " 3 - " ) + XLAT ( " all " ) , nlpage = = 1 ? 0xD8D8C0 : dialogcolor , 8 ) )
2017-03-23 10:53:57 +00:00
getcstat = ' 3 ' ;
2018-09-05 13:18:40 +00:00
if ( i & 1 ) if ( displayfrZH ( xr * 56 , i0 , 1 , vid . fsize , IFM ( keyname ( SDLK_ESCAPE ) + " - " ) + XLAT ( " go back " ) , dialogcolor , 8 ) )
2017-03-23 10:53:57 +00:00
getcstat = ' 0 ' ;
2018-09-05 13:18:40 +00:00
if ( i & 2 ) if ( displayfrZH ( xr * 72 , i0 , 1 , vid . fsize , IFM ( " F1 - " ) + XLAT ( " help " ) , dialogcolor , 8 ) )
2017-03-23 10:53:57 +00:00
getcstat = SDLK_F1 ;
}
2019-08-09 19:00:52 +00:00
EX bool handlePageButtons ( int uni ) {
2017-03-23 10:53:57 +00:00
if ( uni = = ' 1 ' ) nlpage = 1 , wheelshift = 0 ;
else if ( uni = = ' 2 ' ) nlpage = 2 , wheelshift = 0 ;
else if ( uni = = ' 3 ' ) nlpage = 0 , wheelshift = 0 ;
else if ( uni = = PSEUDOKEY_WHEELUP ) wheelshift - - ;
else if ( uni = = PSEUDOKEY_WHEELDOWN ) wheelshift + + ;
else return false ;
return true ;
}
2019-08-09 19:00:52 +00:00
EX void editNumber ( ld & x , ld vmin , ld vmax , ld step , ld dft , string title , string help ) {
2017-07-10 18:47:38 +00:00
ne . editwhat = & x ;
2017-12-22 20:58:09 +00:00
ne . s = disp ( x ) ;
2017-07-10 18:47:38 +00:00
ne . vmin = vmin ;
ne . vmax = vmax ;
ne . step = step ;
ne . dft = dft ;
ne . title = title ;
ne . help = help ;
2018-09-10 14:47:28 +00:00
ne . sc = identity ;
2017-07-10 18:47:38 +00:00
ne . intval = NULL ;
2019-05-04 18:40:46 +00:00
dialogflags = 0 ;
2018-03-24 14:15:30 +00:00
if ( cmode & sm : : SIDE ) dialogflags | = sm : : MAYDARK | sm : : SIDE ;
2017-07-12 17:50:39 +00:00
cmode | = sm : : NUMBER ;
2017-07-10 18:47:38 +00:00
pushScreen ( drawNumberDialog ) ;
2017-09-01 20:06:36 +00:00
reaction = reaction_t ( ) ;
2018-11-06 23:50:03 +00:00
reaction_final = reaction_t ( ) ;
2018-08-01 01:59:20 +00:00
extra_options = reaction_t ( ) ;
2018-05-15 21:26:27 +00:00
numberdark = 0 ;
2018-11-09 19:41:55 +00:00
ne . animatable = true ;
2019-02-17 17:43:39 +00:00
# if CAP_ANIMATIONS
2018-11-09 19:41:55 +00:00
anims : : get_parameter_animation ( x , ne . s ) ;
2019-02-17 17:43:39 +00:00
# endif
2017-07-10 18:47:38 +00:00
}
2019-08-09 19:00:52 +00:00
EX void editNumber ( int & x , int vmin , int vmax , ld step , int dft , string title , string help ) {
2017-07-10 18:47:38 +00:00
editNumber ( ne . intbuf , vmin , vmax , step , dft , title , help ) ;
ne . intbuf = x ; ne . intval = & x ; ne . s = its ( x ) ;
2018-11-09 19:41:55 +00:00
ne . animatable = false ;
2017-07-10 18:47:38 +00:00
}
2019-08-09 19:00:52 +00:00
EX void helpToEdit ( int & x , int vmin , int vmax , int step , int dft ) {
2017-10-08 22:21:39 +00:00
popScreen ( ) ;
string title = " help " ;
if ( help [ 0 ] = = ' @ ' ) {
int iv = help . find ( " \t " ) ;
int id = help . find ( " \n " ) ;
title = help . substr ( iv + 1 , id - iv - 1 ) ;
help = help . substr ( id + 1 ) ;
}
editNumber ( x , vmin , vmax , step , dft , title , help ) ;
}
2017-12-09 03:01:56 +00:00
//-- choose file dialog--
2018-09-05 13:18:40 +00:00
# define CDIR dialogcolor
2018-02-03 13:31:17 +00:00
# define CFILE forecolor
2018-09-05 13:18:40 +00:00
bool filecmp ( const pair < string , color_t > & f1 , const pair < string , color_t > & f2 ) {
2017-12-09 03:01:56 +00:00
if ( f1 . first = = " ../ " ) return true ;
if ( f2 . first = = " ../ " ) return false ;
if ( f1 . second ! = f2 . second )
return f1 . second = = CDIR ;
return f1 . first < f2 . first ;
}
string filecaption , cfileext ;
string * cfileptr ;
bool editext = false ;
2017-12-14 11:10:40 +00:00
bool_reaction_t file_action ;
2018-06-12 20:12:15 +00:00
void handleKeyFile ( int sym , int uni ) ;
2017-12-09 03:01:56 +00:00
2019-08-09 19:00:52 +00:00
EX void drawFileDialog ( ) {
2017-12-09 03:01:56 +00:00
displayfr ( vid . xres / 2 , 30 + vid . fsize , 2 , vid . fsize ,
filecaption , forecolor , 8 ) ;
string & cfile = * cfileptr ;
displayfr ( vid . xres / 2 , 34 + vid . fsize * 2 , 2 , vid . fsize ,
2018-01-05 13:17:40 +00:00
cfile , 0xFFFF00 , 8 ) ;
2017-12-09 03:01:56 +00:00
2018-01-05 13:17:40 +00:00
displayColorButton ( vid . xres * 1 / 4 , 38 + vid . fsize * 3 ,
XLAT ( " F4 = extension " ) , SDLK_F4 , 8 , 0 , editext ? 0xFF00FF : 0xFFFF00 , editext ? 0x800080 : 0x808000 ) ;
2018-01-03 00:08:42 +00:00
displayButton ( vid . xres * 2 / 4 , 38 + vid . fsize * 3 ,
2018-01-05 13:17:40 +00:00
XLAT ( " Enter = choose " ) , SDLK_RETURN , 8 ) ;
2018-01-03 00:08:42 +00:00
displayButton ( vid . xres * 3 / 4 , 38 + vid . fsize * 3 ,
2018-01-05 13:17:40 +00:00
XLAT ( " Esc = cancel " ) , SDLK_ESCAPE , 8 ) ;
2017-12-09 03:01:56 +00:00
v . clear ( ) ;
DIR * d ;
struct dirent * dir ;
string where = " . " ;
2018-06-22 12:47:24 +00:00
for ( int i = 0 ; i < isize ( cfile ) ; i + + )
2017-12-09 03:01:56 +00:00
if ( cfile [ i ] = = ' / ' | | cfile [ i ] = = ' \\ ' )
where = cfile . substr ( 0 , i + 1 ) ;
d = opendir ( where . c_str ( ) ) ;
if ( d ) {
while ( ( dir = readdir ( d ) ) ! = NULL ) {
string s = dir - > d_name ;
if ( s ! = " .. " & & s [ 0 ] = = ' . ' ) ;
2018-06-22 12:47:24 +00:00
else if ( isize ( s ) > 4 & & s . substr ( isize ( s ) - 4 ) = = cfileext )
2017-12-09 03:01:56 +00:00
v . push_back ( make_pair ( s , CFILE ) ) ;
else if ( dir - > d_type & DT_DIR )
v . push_back ( make_pair ( s + " / " , CDIR ) ) ;
}
closedir ( d ) ;
}
sort ( v . begin ( ) , v . end ( ) , filecmp ) ;
int q = v . size ( ) ;
int percolumn = ( vid . yres - 38 ) / ( vid . fsize + 5 ) - 4 ;
int columns = 1 + ( q - 1 ) / percolumn ;
for ( int i = 0 ; i < q ; i + + ) {
int x = 16 + ( vid . xres * ( i / percolumn ) ) / columns ;
int y = 42 + vid . fsize * 4 + ( vid . fsize + 5 ) * ( i % percolumn ) ;
displayColorButton ( x , y , v [ i ] . first , 1000 + i , 0 , 0 , v [ i ] . second , 0xFFFF00 ) ;
}
keyhandler = handleKeyFile ;
}
2019-08-09 19:00:52 +00:00
EX void handleKeyFile ( int sym , int uni ) {
2017-12-09 03:01:56 +00:00
string & s ( * cfileptr ) ;
2018-06-22 12:47:24 +00:00
int i = isize ( s ) - ( editext ? 0 : 4 ) ;
2018-01-05 13:17:40 +00:00
2017-12-14 11:10:40 +00:00
if ( sym = = SDLK_ESCAPE ) {
2017-12-09 03:01:56 +00:00
popScreen ( ) ;
}
2017-12-14 11:10:40 +00:00
else if ( sym = = SDLK_RETURN | | sym = = SDLK_KP_ENTER ) {
// we pop and re-push, in case if action opens something
2017-12-09 03:01:56 +00:00
popScreen ( ) ;
2017-12-14 11:10:40 +00:00
if ( ! file_action ( ) ) pushScreen ( drawFileDialog ) ;
}
2017-12-09 03:01:56 +00:00
else if ( sym = = SDLK_F4 ) {
editext = ! editext ;
}
else if ( sym = = SDLK_BACKSPACE & & i ) {
s . erase ( i - 1 , 1 ) ;
}
else if ( uni > = 32 & & uni < 127 ) {
s . insert ( i , s0 + char ( uni ) ) ;
}
2018-06-22 12:47:24 +00:00
else if ( uni > = 1000 & & uni < = 1000 + isize ( v ) ) {
2017-12-09 03:01:56 +00:00
string where = " " , what = s , whereparent = " ../ " ;
2018-06-22 12:47:24 +00:00
for ( int i = 0 ; i < isize ( s ) ; i + + )
2017-12-09 03:01:56 +00:00
if ( s [ i ] = = ' / ' ) {
if ( i > = 2 & & s . substr ( i - 2 , 3 ) = = " ../ " )
whereparent = s . substr ( 0 , i + 1 ) + " ../ " ;
else
whereparent = where ;
where = s . substr ( 0 , i + 1 ) , what = s . substr ( i + 1 ) ;
}
int i = uni - 1000 ;
if ( v [ i ] . first = = " ../ " ) {
s = whereparent + what ;
}
else if ( v [ i ] . second = = CDIR )
s = where + v [ i ] . first + what ;
else
s = where + v [ i ] . first ;
}
2018-06-12 20:12:15 +00:00
return ;
2017-12-09 03:01:56 +00:00
}
2019-08-09 19:00:52 +00:00
EX void openFileDialog ( string & filename , string fcap , string ext , bool_reaction_t action ) {
2017-12-09 03:01:56 +00:00
cfileptr = & filename ;
filecaption = fcap ;
cfileext = ext ;
2017-12-14 11:10:40 +00:00
file_action = action ;
2017-12-09 03:01:56 +00:00
pushScreen ( dialog : : drawFileDialog ) ;
}
// infix/v/vpush
2019-08-09 19:00:52 +00:00
EX string infix ;
2017-12-09 03:01:56 +00:00
2019-08-09 19:00:52 +00:00
EX bool hasInfix ( const string & s ) {
2017-12-09 03:01:56 +00:00
if ( infix = = " " ) return true ;
string t = " " ;
2018-06-22 12:47:24 +00:00
for ( int i = 0 ; i < isize ( s ) ; i + + ) {
2017-12-09 03:01:56 +00:00
char c = s [ i ] ;
char tt = 0 ;
if ( c > = ' a ' & & c < = ' z ' ) tt + = c - 32 ;
else if ( c > = ' A ' & & c < = ' Z ' ) tt + = c ;
else if ( c = = ' @ ' ) tt + = c ;
if ( tt ) t + = tt ;
}
return t . find ( infix ) ! = string : : npos ;
}
2019-08-09 19:00:52 +00:00
EX bool editInfix ( int uni ) {
2017-12-09 03:01:56 +00:00
if ( uni > = ' A ' & & uni < = ' Z ' ) infix + = uni ;
else if ( uni > = ' a ' & & uni < = ' z ' ) infix + = uni - 32 ;
2018-06-22 12:47:24 +00:00
else if ( infix ! = " " & & uni = = 8 ) infix = infix . substr ( 0 , isize ( infix ) - 1 ) ;
2017-12-09 03:01:56 +00:00
else if ( infix ! = " " & & uni ! = 0 ) infix = " " ;
else return false ;
return true ;
}
2019-08-09 19:00:52 +00:00
EX vector < pair < string , color_t > > v ;
2017-12-09 03:01:56 +00:00
2019-08-09 19:00:52 +00:00
EX void vpush ( color_t color , const char * name ) {
2017-12-09 03:01:56 +00:00
string s = XLATN ( name ) ;
if ( ! hasInfix ( s ) ) return ;
2018-09-05 13:18:40 +00:00
dialog : : v . push_back ( make_pair ( s , color ) ) ;
2017-12-09 03:01:56 +00:00
}
2018-11-06 23:50:03 +00:00
int editpos = 0 ;
2019-08-09 19:00:52 +00:00
EX string * edited_string ;
2018-11-06 23:50:03 +00:00
2019-08-09 19:00:52 +00:00
EX string view_edited_string ( ) {
2018-11-06 23:50:03 +00:00
string cs = * edited_string ;
if ( editpos < 0 ) editpos = 0 ;
if ( editpos > isize ( cs ) ) editpos = isize ( cs ) ;
cs . insert ( editpos , " ° " ) ;
return cs ;
}
2019-08-09 19:00:52 +00:00
EX void start_editing ( string & s ) {
2018-11-06 23:50:03 +00:00
edited_string = & s ;
editpos = isize ( s ) ;
}
2018-11-24 21:49:45 +00:00
2019-08-09 19:00:52 +00:00
EX string editchecker ( int sym , int uni ) {
2018-11-24 21:49:45 +00:00
if ( uni > = 32 & & uni < 127 ) return string ( " " ) + char ( uni ) ;
return " " ;
}
2018-11-06 23:50:03 +00:00
2019-08-09 19:00:52 +00:00
EX bool handle_edit_string ( int sym , int uni , function < string ( int , int ) > checker IS ( editchecker ) ) {
2018-11-06 23:50:03 +00:00
auto & es = * edited_string ;
2018-11-24 21:49:45 +00:00
string u2 ;
2018-12-06 11:31:51 +00:00
if ( DKEY = = SDLK_LEFT ) editpos - - ;
else if ( DKEY = = SDLK_RIGHT ) editpos + + ;
2018-11-06 23:50:03 +00:00
else if ( uni = = 8 ) {
if ( editpos = = 0 ) return true ;
es . replace ( editpos - 1 , 1 , " " ) ;
editpos - - ;
}
2018-11-24 21:49:45 +00:00
else if ( ( u2 = checker ( sym , uni ) ) ! = " " ) {
for ( char c : u2 ) {
es . insert ( editpos , 1 , c ) ;
editpos + + ;
}
2018-11-06 23:50:03 +00:00
}
else return false ;
return true ;
}
2019-08-09 19:00:52 +00:00
EX void string_edit_dialog ( ) {
2018-11-06 23:50:03 +00:00
cmode = sm : : NUMBER | dialogflags ;
2019-07-12 21:13:35 +00:00
if ( numberdark < DONT_SHOW )
2018-11-06 23:50:03 +00:00
gamescreen ( numberdark ) ;
init ( ne . title ) ;
addInfo ( view_edited_string ( ) ) ;
addBreak ( 100 ) ;
2019-07-03 05:31:09 +00:00
formula_keyboard ( true ) ;
addBreak ( 100 ) ;
2018-11-06 23:50:03 +00:00
dialog : : addBack ( ) ;
addBreak ( 100 ) ;
if ( ne . help ! = " " ) {
addHelp ( ne . help ) ;
}
if ( extra_options ) extra_options ( ) ;
display ( ) ;
keyhandler = [ ] ( int sym , int uni ) {
handleNavigation ( sym , uni ) ;
if ( handle_edit_string ( sym , uni ) ) ;
else if ( doexiton ( sym , uni ) ) {
popScreen ( ) ;
if ( reaction_final ) reaction_final ( ) ;
}
} ;
}
2019-08-09 19:00:52 +00:00
EX void edit_string ( string & s , string title , string help ) {
2018-11-06 23:50:03 +00:00
start_editing ( s ) ;
ne . title = title ;
ne . help = help ;
2019-05-04 18:40:46 +00:00
dialogflags = 0 ;
2018-11-06 23:50:03 +00:00
if ( cmode & sm : : SIDE ) dialogflags | = sm : : MAYDARK | sm : : SIDE ;
cmode | = sm : : NUMBER ;
pushScreen ( string_edit_dialog ) ;
reaction = reaction_t ( ) ;
extra_options = reaction_t ( ) ;
numberdark = 0 ;
}
2019-08-09 19:00:52 +00:00
EX void confirm_dialog ( const string & text , const reaction_t & act ) {
2018-12-14 17:21:52 +00:00
gamescreen ( 1 ) ;
dialog : : addBreak ( 250 ) ;
dialog : : init ( XLAT ( " WARNING " ) , 0xFF0000 , 150 , 100 ) ;
2018-12-14 17:22:12 +00:00
dialog : : addHelp ( text ) ;
2018-12-14 17:21:52 +00:00
dialog : : addItem ( XLAT ( " YES " ) , ' y ' ) ;
auto yes = [ act ] ( ) { popScreen ( ) ; act ( ) ; } ;
dialog : : add_action ( yes ) ;
dialog : : add_key_action ( SDLK_RETURN , yes ) ;
dialog : : addItem ( XLAT ( " NO " ) , ' n ' ) ;
dialog : : add_action ( [ ] ( ) { popScreen ( ) ; } ) ;
dialog : : display ( ) ;
}
2019-08-09 19:00:52 +00:00
EX void addBoolItem_action ( const string & s , bool & b , char c ) {
2019-05-03 09:44:15 +00:00
dialog : : addBoolItem ( s , b , c ) ;
dialog : : add_action ( [ & b ] { b = ! b ; } ) ;
}
2019-05-03 10:11:40 +00:00
2019-08-09 19:00:52 +00:00
EX void addBoolItem_action_neg ( const string & s , bool & b , char c ) {
2019-05-03 10:11:40 +00:00
dialog : : addBoolItem ( s , ! b , c ) ;
dialog : : add_action ( [ & b ] { b = ! b ; } ) ;
}
2019-08-09 19:00:52 +00:00
2020-05-31 14:43:36 +00:00
EX bool cheat_forbidden ( ) {
if ( tactic : : on & & ! cheater ) {
addMessage ( XLAT ( " Not available in the pure tactics mode! " ) ) ;
return true ;
}
if ( daily : : on ) {
addMessage ( XLAT ( " Not available in the daily challenge! " ) ) ;
return true ;
}
return false ;
}
2020-07-12 18:49:06 +00:00
EX void add_action_confirmed ( const reaction_t & act ) {
dialog : : add_action ( dialog : : add_confirmation ( act ) ) ;
}
2020-05-31 14:43:36 +00:00
2019-08-09 19:00:52 +00:00
# if HDR
template < class T > void addBoolItem_choice ( const string & s , T & b , T val , char c ) {
addBoolItem ( s , b = = val , c ) ;
add_action ( [ & b , val ] { b = val ; } ) ;
}
inline void cheat_if_confirmed ( const reaction_t & act ) {
2020-05-31 14:43:36 +00:00
if ( cheat_forbidden ( ) )
return ;
2019-08-09 19:00:52 +00:00
if ( needConfirmationEvenIfSaved ( ) ) pushScreen ( [ act ] ( ) { confirm_dialog ( XLAT ( " This will enable the cheat mode, making this game ineligible for scoring. Are you sure? " ) , act ) ; } ) ;
else act ( ) ;
}
inline void do_if_confirmed ( const reaction_t & act ) {
if ( needConfirmationEvenIfSaved ( ) ) pushScreen ( [ act ] ( ) { confirm_dialog ( XLAT ( " This will end your current game and start a new one. Are you sure? " ) , act ) ; } ) ;
else act ( ) ;
}
2020-04-17 18:34:49 +00:00
inline void push_confirm_dialog ( const reaction_t & act , const string & s ) {
pushScreen ( [ act , s ] ( ) { confirm_dialog ( s , act ) ; } ) ;
}
2019-08-09 19:00:52 +00:00
inline reaction_t add_confirmation ( const reaction_t & act ) {
return [ act ] { do_if_confirmed ( act ) ; } ;
}
# endif
2020-02-23 01:51:27 +00:00
}
2018-06-10 23:58:31 +00:00
}