change newgame api names; implement juggle; change getter to take a

callback to copy strings that will go out of scope before the getter
returns (e.g. wince).  Use changed API in gtk dialog.
This commit is contained in:
ehouse 2006-06-07 01:45:03 +00:00
parent 6c7e4b37f4
commit 00291dc997
3 changed files with 248 additions and 81 deletions

View file

@ -20,7 +20,7 @@
#include "nwgamest.h"
#include "strutils.h"
#ifdef CPLUS
extern "C" {
@ -52,11 +52,11 @@ static void adjustAllRows( NewGameCtx* ngc, XP_Bool force );
static void adjustOneRow( NewGameCtx* ngc, XP_U16 player, XP_Bool force );
NewGameCtx*
gamedlg_make( MPFORMAL XP_Bool isNewGame,
NewGameEnableColProc enableColProc,
NewGameEnableAttrProc enableAttrProc,
NewGameGetColProc getColProc, NewGameSetColProc setColProc,
NewGameSetAttrProc setAttrProc, void* closure )
newg_make( MPFORMAL XP_Bool isNewGame,
NewGameEnableColProc enableColProc,
NewGameEnableAttrProc enableAttrProc,
NewGameGetColProc getColProc, NewGameSetColProc setColProc,
NewGameSetAttrProc setAttrProc, void* closure )
{
NewGameCtx* result = XP_MALLOC( mpool, sizeof(*result) );
XP_MEMSET( result, 0, sizeof(*result) );
@ -72,10 +72,10 @@ gamedlg_make( MPFORMAL XP_Bool isNewGame,
MPASSIGN(result->mpool, mpool);
return result;
} /* gamedlg_make */
} /* newg_make */
void
gamedlg_destroy( NewGameCtx* ngc )
newg_destroy( NewGameCtx* ngc )
{
#ifdef PLATFORM_PALM
for ( i = 0; i < MAX_NUM_PLAYERS; ++i ) {
@ -85,10 +85,10 @@ gamedlg_destroy( NewGameCtx* ngc )
}
}
#endif
} /* gamedlg_destroy */
} /* newg_destroy */
void
gamedlg_load( NewGameCtx* ngc, const CurGameInfo* gi )
newg_load( NewGameCtx* ngc, const CurGameInfo* gi )
{
void* closure = ngc->closure;
NGValue cValue;
@ -123,50 +123,78 @@ gamedlg_load( NewGameCtx* ngc, const CurGameInfo* gi )
}
adjustAllRows( ngc, XP_TRUE );
} /* gamedlg_load */
} /* newg_load */
typedef struct NGCopyClosure {
XP_U16 player;
NewGameColumn col;
NewGameCtx* ngc;
CurGameInfo* gi;
} NGCopyClosure;
static void
cpToGI( NGValue value, const void* cbClosure )
{
NGCopyClosure* cpcl = (NGCopyClosure*)cbClosure;
LocalPlayer* pl = &cpcl->gi->players[cpcl->player];
XP_UCHAR** strAddr = NULL;
switch ( cpcl->col ) {
#ifndef XWFEATURE_STANDALONE_ONLY
case NG_COL_LOCAL:
pl->isLocal = value.ng_bool;
break;
#endif
case NG_COL_NAME:
strAddr = &pl->name;
break;
case NG_COL_PASSWD:
strAddr = &pl->password;
break;
case NG_COL_ROBOT:
pl->isRobot = value.ng_bool;
break;
default:
XP_ASSERT(0);
}
if ( !!strAddr ) {
replaceStringIfDifferent( MPPARM(cpcl->ngc->mpool) strAddr,
value.ng_cp );
}
} /* cpToGI */
void
gamedlg_store( NewGameCtx* ngc, CurGameInfo* gi )
newg_store( NewGameCtx* ngc, CurGameInfo* gi )
{
NGValue aValue;
XP_U16 nPlayers, i;
XP_U16 nPlayers;
void* closure = ngc->closure;
NGCopyClosure cpcl;
cpcl.ngc = ngc;
cpcl.gi = gi;
gi->nPlayers = nPlayers = ngc->nPlayers;
gi->serverRole = ngc->role;
for ( i = 0; i < nPlayers; ++i ) {
NGValue cValue;
#ifndef XWFEATURE_STANDALONE_ONLY
(*ngc->getColProc)( closure, i, NG_COL_LOCAL, &cValue );
gi->players[i].isLocal = cValue.ng_bool;
#endif
(*ngc->getColProc)( closure, i, NG_COL_NAME, &cValue );
replaceStringIfDifferent( MPPARM(ngc->mpool) &gi->players[i].name,
cValue.ng_cp );
XP_LOGF( "copied %s", gi->players[i].name );
(*ngc->getColProc)( closure, i, NG_COL_PASSWD, &cValue );
replaceStringIfDifferent( MPPARM(ngc->mpool) &gi->players[i].password,
cValue.ng_cp );
(*ngc->getColProc)( closure, i, NG_COL_ROBOT, &cValue );
gi->players[i].isRobot = cValue.ng_bool;
for ( cpcl.player = 0; cpcl.player < nPlayers; ++cpcl.player ) {
for ( cpcl.col = 0; cpcl.col < NG_NUM_COLS; ++cpcl.col ) {
(*ngc->getColProc)( closure, cpcl.player, cpcl.col,
cpToGI, &cpcl );
}
}
} /* gamedlg_store */
} /* newg_store */
void
gamedlg_colChanged( NewGameCtx* ngc, XP_U16 player, NewGameColumn col,
NGValue value )
newg_colChanged( NewGameCtx* ngc, XP_U16 player, NewGameColumn col,
NGValue value )
{
XP_ASSERT( player < ngc->nPlayers );
adjustOneRow( ngc, player, XP_FALSE );
}
void
gamedlg_attrChanged( NewGameCtx* ngc, NewGameAttr attr, NGValue value )
newg_attrChanged( NewGameCtx* ngc, NewGameAttr attr, NGValue value )
{
XP_LOGF( "%s(%d)", __FUNCTION__ );
if ( attr == NG_ATTR_NPLAYERS ) {
@ -179,6 +207,130 @@ gamedlg_attrChanged( NewGameCtx* ngc, NewGameAttr attr, NGValue value )
adjustAllRows( ngc, XP_FALSE );
}
/* This belongs as a util the tray juggling code can also call */
static void
randIntArray( XP_U16* rnums, XP_U16 count )
{
XP_U16 array[count]; /* C99 thing */
XP_U16 i;
for ( i = 0; i < count; ++i ) {
rnums[i] = array[i] = i; /* initial state for comparison */
}
do {
for ( i = 1; i < count; ++i ) {
XP_U16 rIndex = ((XP_U16)XP_RANDOM()) % i;
XP_U16 tmp = rnums[rIndex];
rnums[rIndex] = rnums[i];
rnums[i] = tmp;
}
} while ( XP_MEMCMP( rnums, array, count * sizeof(array[0]) ) == 0 );
} /* randIntArray */
typedef struct DeepValue {
NGValue value;
NewGameColumn col;
MPSLOT
} DeepValue;
static void
deepCopy( NGValue value, const void* closure )
{
DeepValue* dvp = (DeepValue*)closure;
switch ( dvp->col ) {
case NG_COL_ROBOT:
case NG_COL_LOCAL:
dvp->value.ng_bool = value.ng_bool;
break;
case NG_COL_NAME:
case NG_COL_PASSWD:
dvp->value.ng_cp = copyString( MPPARM(dvp->mpool) value.ng_cp );
break;
default:
XP_ASSERT(0);
}
}
static void
deepFree( DeepValue* dvp )
{
if ( dvp->col == NG_COL_NAME || dvp->col == NG_COL_PASSWD ) {
XP_FREE( dvp->mpool, (void*)dvp->value.ng_cp );
}
}
static void
copyFromTo( NewGameCtx* ngc, XP_U16 srcPlayer, XP_U16 destPlayer )
{
DeepValue dValue;
void* closure = ngc->closure;
MPASSIGN(dValue.mpool, ngc->mpool);
for ( dValue.col = 0; dValue.col < NG_NUM_COLS; ++dValue.col ) {
(*ngc->getColProc)( closure, srcPlayer, dValue.col, deepCopy, &dValue );
(*ngc->setColProc)( closure, destPlayer, dValue.col, dValue.value );
deepFree( &dValue );
}
} /* copyFromTo */
void
newg_juggle( NewGameCtx* ngc )
{
XP_U16 nPlayers = ngc->nPlayers;
/* NewGameColumn strCols[] = { NG_COL_NAME, NG_COL_PASSWD }; */
LOG_FUNC();
if ( nPlayers > 1 ) {
XP_U16 pos[MAX_NUM_PLAYERS];
void* closure = ngc->closure;
DeepValue tmpValues[NG_NUM_COLS];
XP_U16 cur;
NewGameColumn col;
/* Get a randomly juggled array of numbers 0..nPlayers-1. Then the number
at pos[n] inicates where the entry currently at n should be. The first
must be copied into tmp, after which each can be moved in sequence
before tmp is copied into the last empty slot. */
randIntArray( pos, nPlayers );
XP_LOGF( "%s: saving off player %d as tmp", __FUNCTION__, pos[0] );
for ( col = 0; col < NG_NUM_COLS; ++col ) {
tmpValues[col].col = col;
MPASSIGN( tmpValues[col].mpool, ngc->mpool );
(*ngc->getColProc)(closure, pos[0], col, deepCopy, &tmpValues[col] );
}
/* Strings must be copied */
/* for ( col = 0; col < sizeof(strCols)/sizeof(strCols[0]); ++col ) { */
/* NewGameColumn strCol = strCols[col]; */
/* tmpValues[strCol].ng_cp = copyString( MPPARM(ngc->mpool) */
/* tmpValues[strCol].ng_cp ); */
/* } */
cur = 0;
while ( ++cur < nPlayers ) {
XP_LOGF( "%s: copying player %d to player %d", __FUNCTION__,
pos[cur], pos[cur-1] );
copyFromTo( ngc, pos[cur], pos[cur-1] );
}
--cur;
XP_LOGF( "%s: writing tmp back to player %d", __FUNCTION__,
pos[cur] );
for ( col = 0; col < NG_NUM_COLS; ++col ) {
(*ngc->setColProc)(closure, pos[cur], col, tmpValues[col].value );
deepFree( &tmpValues[col] );
}
/* copied strings must be freed */
/* for ( col = 0; col < sizeof(strCols)/sizeof(strCols[0]); ++col ) { */
/* NewGameColumn strCol = strCols[col]; */
/* XP_FREE( ngc->mpool, (void*)tmpValues[strCol].ng_cp ); */
/* } */
}
} /* newg_juggle */
static void
enableOne( NewGameCtx* ngc, XP_U16 player, NewGameColumn col,
XP_Bool enable, XP_Bool force )
@ -206,7 +358,8 @@ adjustOneRow( NewGameCtx* ngc, XP_U16 player, XP_Bool force )
NewGameColumn col;
XP_MEMSET( enable, 0, sizeof(enable) );
XP_Bool isLocal = XP_TRUE;
NGValue value;
DeepValue dValue;
/* NGValue value; */
/* If there aren't this many players, all are disabled */
if ( player >= ngc->nPlayers ) {
@ -218,8 +371,9 @@ adjustOneRow( NewGameCtx* ngc, XP_U16 player, XP_Bool force )
/* If standalone or client, local is disabled */
if ( ngc->role == SERVER_ISSERVER ) {
enable[NG_COL_LOCAL] = XP_TRUE;
(*ngc->getColProc)( ngc->closure, player, NG_COL_LOCAL, &value );
isLocal = value.ng_bool;
dValue.col = NG_COL_LOCAL;
(*ngc->getColProc)( ngc->closure, player, NG_COL_LOCAL, deepCopy, &dValue );
isLocal = dValue.value.ng_bool;
}
#endif
@ -227,10 +381,10 @@ adjustOneRow( NewGameCtx* ngc, XP_U16 player, XP_Bool force )
if ( isLocal ) {
enable[NG_COL_NAME] = XP_TRUE;
enable[NG_COL_ROBOT] = XP_TRUE;
(*ngc->getColProc)( ngc->closure, player, NG_COL_ROBOT,
&value );
if ( !value.ng_bool ) {
dValue.col = NG_COL_ROBOT;
(*ngc->getColProc)( ngc->closure, player, NG_COL_ROBOT, deepCopy,
&dValue );
if ( !dValue.value.ng_bool ) {
enable[NG_COL_PASSWD] = XP_TRUE;
}
}

View file

@ -69,8 +69,10 @@ typedef void (*NewGameEnableAttrProc)( void* closure, NewGameAttr attr,
XP_Bool enable );
/* Get the contents of a control. Type of param "value" is either
boolean or char* */
typedef void (*NgCpCallbk)( NGValue value, const void* cpClosure );
typedef void (*NewGameGetColProc)( void* closure, XP_U16 player,
NewGameColumn col, NGValue* value );
NewGameColumn col,
NgCpCallbk cpcb, const void* cbClosure );
/* Set the contents of a control. Type of param "value" is either
boolean or char* */
typedef void (*NewGameSetColProc)( void* closure, XP_U16 player,
@ -80,24 +82,24 @@ typedef void (*NewGameSetAttrProc)(void* closure, NewGameAttr attr,
const NGValue value );
NewGameCtx* gamedlg_make( MPFORMAL XP_Bool isNewGame,
NewGameEnableColProc enableColProc,
NewGameEnableAttrProc enableAttrProc,
NewGameGetColProc getColProc,
NewGameSetColProc setColProc,
NewGameSetAttrProc setAttrProc,
void* closure );
void gamedlg_destroy( NewGameCtx* ngc );
NewGameCtx* newg_make( MPFORMAL XP_Bool isNewGame,
NewGameEnableColProc enableColProc,
NewGameEnableAttrProc enableAttrProc,
NewGameGetColProc getColProc,
NewGameSetColProc setColProc,
NewGameSetAttrProc setAttrProc,
void* closure );
void newg_destroy( NewGameCtx* ngc );
void gamedlg_load( NewGameCtx* ngc, const CurGameInfo* gi );
void gamedlg_store( NewGameCtx* ngc, CurGameInfo* gi );
void newg_load( NewGameCtx* ngc, const CurGameInfo* gi );
void newg_store( NewGameCtx* ngc, CurGameInfo* gi );
void gamedlg_colChanged( NewGameCtx* ngc, XP_U16 player, NewGameColumn col,
NGValue value );
void gamedlg_attrChanged( NewGameCtx* ngc, NewGameAttr attr,
NGValue value );
void newg_colChanged( NewGameCtx* ngc, XP_U16 player, NewGameColumn col,
NGValue value );
void newg_attrChanged( NewGameCtx* ngc, NewGameAttr attr,
NGValue value );
void gamedlg_juggle( NewGameCtx* ngc );
void newg_juggle( NewGameCtx* ngc );
EXTERN_C_END

View file

@ -78,7 +78,7 @@ nplayers_menu_select( GtkWidget* item, GtkNewGameState* state )
gtk_container_foreach( GTK_CONTAINER(item->parent), countBeforeSame,
&pair );
value.ng_u16 = pair.index + 1;
gamedlg_attrChanged( state->newGameCtxt, NG_ATTR_NPLAYERS, value );
newg_attrChanged( state->newGameCtxt, NG_ATTR_NPLAYERS, value );
} /* nplayers_menu_select */
static void
@ -95,7 +95,7 @@ role_menu_select( GtkWidget* item, GtkNewGameState* state )
XP_ASSERT( i < 3 ); /* did we not find it? */
value.ng_role = (Connectedness)i;
gamedlg_attrChanged( state->newGameCtxt, NG_ATTR_ROLE, value );
newg_attrChanged( state->newGameCtxt, NG_ATTR_ROLE, value );
} /* role_menu_select */
static void
@ -112,7 +112,7 @@ callChangedWithIndex( GtkNewGameState* state, GtkWidget* item,
XP_ASSERT( player < MAX_NUM_PLAYERS );
value.ng_bool = gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(item) );
gamedlg_colChanged( state->newGameCtxt, player, NG_COL_ROBOT, value );
newg_colChanged( state->newGameCtxt, player, NG_COL_ROBOT, value );
} /* callChangedWithIndex */
static void
@ -121,6 +121,12 @@ handle_robot_toggled( GtkWidget* item, GtkNewGameState* state )
callChangedWithIndex( state, item, state->robotChecks, NG_COL_ROBOT );
}
static void
handle_juggle( GtkWidget* item, GtkNewGameState* state )
{
newg_juggle( state->newGameCtxt );
}
#ifndef XWFEATURE_STANDALONE_ONLY
static void
handle_local_toggled( GtkWidget* item, GtkNewGameState* state )
@ -253,6 +259,11 @@ makeNewGameDialog( GtkNewGameState* state )
gtk_widget_show( opt );
gtk_box_pack_start( GTK_BOX(hbox), opt, FALSE, TRUE, 0 );
gtk_box_pack_start( GTK_BOX(hbox),
makeButton( "Juggle", GTK_SIGNAL_FUNC(handle_juggle),
state ),
FALSE, TRUE, 0 );
gtk_widget_show( hbox );
gtk_box_pack_start( GTK_BOX(vbox), hbox, FALSE, TRUE, 0 );
@ -432,12 +443,10 @@ gtk_newgame_col_set( void* closure, XP_U16 player, NewGameColumn col,
static void
gtk_newgame_col_get( void* closure, XP_U16 player, NewGameColumn col,
NGValue* value )
NgCpCallbk cpcb, const void* cbClosure )
{
GtkNewGameState* state = (GtkNewGameState*)closure;
XP_LOGF( "%s: player=%d; col = %d", __FUNCTION__,
player, (int)col );
NGValue value;
GtkWidget* widget = widgetForCol( state, player, col );
switch ( col ) {
@ -445,14 +454,16 @@ gtk_newgame_col_get( void* closure, XP_U16 player, NewGameColumn col,
case NG_COL_LOCAL:
#endif
case NG_COL_ROBOT:
value->ng_bool =
value.ng_bool =
gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(widget));
break;
case NG_COL_PASSWD:
case NG_COL_NAME:
value->ng_cp = gtk_entry_get_text( GTK_ENTRY(widget) );
value.ng_cp = gtk_entry_get_text( GTK_ENTRY(widget) );
break;
}
(*cpcb)( value, cbClosure );
} /* gtk_newgame_col_get */
static void
@ -475,28 +486,28 @@ newGameDialog( GtkAppGlobals* globals/* , GtkGameInfo* gameInfo */ )
XP_MEMSET( &state, 0, sizeof(state) );
state.globals = globals;
state.newGameCtxt = gamedlg_make( MPPARM(globals->cGlobals.params
->util->mpool)
XP_TRUE, /* does gtk have concept of new
game yet? */
gtk_newgame_col_enable,
gtk_newgame_attr_enable,
gtk_newgame_col_get,
gtk_newgame_col_set,
gtk_newgame_attr_set,
&state );
state.newGameCtxt = newg_make( MPPARM(globals->cGlobals.params
->util->mpool)
XP_TRUE, /* does gtk have concept of new
game yet? */
gtk_newgame_col_enable,
gtk_newgame_attr_enable,
gtk_newgame_col_get,
gtk_newgame_col_set,
gtk_newgame_attr_set,
&state );
/* returns when button handler calls gtk_main_quit */
do {
GtkWidget* dialog = makeNewGameDialog( &state );
state.revert = FALSE;
gamedlg_load( state.newGameCtxt,
newg_load( state.newGameCtxt,
&globals->cGlobals.params->gi );
gtk_main();
if ( !state.cancelled && !state.revert ) {
gamedlg_store( state.newGameCtxt, &globals->cGlobals.params->gi );
newg_store( state.newGameCtxt, &globals->cGlobals.params->gi );
}
gtk_widget_destroy( dialog );