xwords/xwords4/wince/ceginfo.c

754 lines
25 KiB
C
Executable file

/* -*-mode: C; fill-column: 77; c-basic-offset: 4; compile-command: "make TARGET_OS=wince DEBUG=TRUE"; -*- */
/*
* Copyright 2002-2009 by Eric House (xwords@eehouse.org). All rights
* reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <stdio.h> /* swprintf */
#include "ceginfo.h"
#include "cemain.h"
#include "ceutil.h"
#include "cedict.h"
#include "cecondlg.h"
#include "strutils.h"
#include "cedebug.h"
#include "strutils.h"
#include "ceresstr.h"
#define NUM_COLS 4
#define MENUDICTS_INCR 16
static XP_S16
findInsertPoint( const wchar_t* wPath, wchar_t** menuDicts,
XP_U16 nMenuDicts )
{
XP_S16 loc = 0; /* simple case: nothing here */
if ( nMenuDicts > 0 ) {
wchar_t thisShortBuf[CE_MAX_PATH_LEN+1];
wchar_t* thisShortName = wbname( thisShortBuf, sizeof(thisShortBuf),
wPath );
/* If the short path doesn't already exist, find where it belongs. This
is wasteful if we're doing this a lot since the short path isn't
cached. */
for ( /* loc = 0*/; loc < nMenuDicts; ++loc ) {
wchar_t oneShortBuf[CE_MAX_PATH_LEN+1];
wchar_t* oneShortName = wbname( oneShortBuf, sizeof(oneShortBuf),
menuDicts[loc] );
int diff = _wcsicmp( thisShortName, oneShortName );
if ( diff > 0 ) {
continue;
} else if ( diff == 0 ) {
loc = -1;
}
break;
}
}
return loc;
} /* findInsertPoint */
static XP_Bool
addDictToState( const wchar_t* wPath, XP_U16 XP_UNUSED(index), void* ctxt )
{
GameInfoState* state = (GameInfoState*)ctxt;
/* Let's display only the short form, but save the whole path */
wchar_t* wstr;
XP_U16 len;
XP_S16 loc; /* < 0 means skip it */
loc = findInsertPoint( wPath, state->menuDicts,
state->nMenuDicts );
if ( loc >= 0 ) {
/* make a copy of the long name */
len = wcslen( wPath ) + 1;
wstr = (wchar_t*)XP_MALLOC( state->dlgHdr.globals->mpool,
len * sizeof(wstr[0]) );
XP_MEMCPY( wstr, wPath, len*sizeof(wstr[0]) );
if ( !state->menuDicts ) {
XP_ASSERT( state->nMenuDicts == 0 );
XP_ASSERT( state->capMenuDicts == 0 );
state->capMenuDicts = MENUDICTS_INCR;
state->menuDicts
= (wchar_t**)XP_MALLOC( state->dlgHdr.globals->mpool,
state->capMenuDicts
* sizeof(state->menuDicts[0]) );
} else if ( state->nMenuDicts == state->capMenuDicts ) {
state->capMenuDicts += MENUDICTS_INCR;
state->menuDicts
= (wchar_t**)XP_REALLOC( state->dlgHdr.globals->mpool,
state->menuDicts,
state->capMenuDicts
* sizeof(state->menuDicts[0]) );
}
if ( loc < state->nMenuDicts ) {
XP_MEMMOVE( &state->menuDicts[loc+1], &state->menuDicts[loc],
(state->nMenuDicts - loc)
* sizeof(state->menuDicts[0]) );
}
state->menuDicts[loc] = wstr;
++state->nMenuDicts;
}
return XP_FALSE;
} /* addDictToState */
static void
addDictsToMenu( GameInfoState* state )
{
wchar_t* shortname;
wchar_t shortPath[CE_MAX_PATH_LEN+1];
XP_U16 i, nMenuDicts = state->nMenuDicts;
XP_S16 sel = 0;
CEAppGlobals* globals = state->dlgHdr.globals;
/* insert the short names in the menu */
for ( i = 0; i < nMenuDicts; ++i ) {
wchar_t* wPath = state->menuDicts[i];
shortname = wbname( shortPath, sizeof(shortPath), wPath );
SendDlgItemMessage( state->dlgHdr.hDlg, state->dictListId,
ADDSTRING(globals), 0, (long)shortname );
if ( state->newDictName[0] != 0 && sel == 0 ) {
XP_UCHAR buf[CE_MAX_PATH_LEN+1];
WideCharToMultiByte( CP_ACP, 0, wPath, -1, buf, sizeof(buf),
NULL, NULL );
if ( 0 == XP_STRCMP( buf, state->newDictName ) ) {
sel = i;
}
}
}
SendDlgItemMessage( state->dlgHdr.hDlg, state->dictListId,
SETCURSEL(globals), sel, 0L );
} /* addDictsToMenu */
static void
cleanupGameInfoState( GameInfoState* state )
{
if ( !!state->menuDicts ) {
XP_U16 nMenuDicts = state->nMenuDicts;
XP_U16 i;
for ( i = 0; i < nMenuDicts; ++i ) {
XP_FREE( state->dlgHdr.globals->mpool, state->menuDicts[i] );
}
XP_FREE( state->dlgHdr.globals->mpool, state->menuDicts );
state->menuDicts = NULL;
}
if ( !!state->moveIds ) {
XP_FREE( state->dlgHdr.globals->mpool, state->moveIds );
state->moveIds = NULL;
}
} /* cleanupGameInfoState */
static void
loadFromGameInfo( GameInfoState* state )
{
XP_U16 ii;
CEAppGlobals* globals = state->dlgHdr.globals;
CurGameInfo* gi = &globals->gameInfo;
#ifndef XWFEATURE_STANDALONE_ONLY
XP_U16 role_ids[] = { IDS_ROLE_STANDALONE, IDS_ROLE_HOST, IDS_ROLE_GUEST };
for ( ii = 0; ii < VSIZE(role_ids); ++ii ) {
const wchar_t* wstr = ceGetResStringL( globals, role_ids[ii] );
SendDlgItemMessage( state->dlgHdr.hDlg, state->roleComboId,
ADDSTRING(globals), 0, (long)wstr );
}
#endif
for ( ii = 0; ii < MAX_NUM_PLAYERS; ++ii ) {
wchar_t widebuf[8];
/* put a string in the moronic combobox */
swprintf( widebuf, L"%d", ii + 1 );
SendDlgItemMessage( state->dlgHdr.hDlg, state->nPlayersId,
ADDSTRING(globals), 0, (long)widebuf );
}
newg_load( state->newGameCtx, gi );
#ifndef STUBBED_DICT
if ( !!gi->dictName ) {
XP_MEMCPY( state->newDictName, gi->dictName,
(XP_U16)XP_STRLEN(gi->dictName)+1 );
}
if ( state->isNewGame ) {
(void)ceLocateNDicts( globals, CE_MAXDICTS, addDictToState, state );
} else {
wchar_t wPath[CE_MAX_PATH_LEN+1];
XP_ASSERT( gi->dictName[0] != '\0' );
MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, gi->dictName, -1,
wPath, VSIZE(wPath) );
(void)addDictToState( wPath, 0, state );
}
addDictsToMenu( state );
#endif
if ( !state->isNewGame ) {
ceEnOrDisable( state->dlgHdr.hDlg, state->dictListId, XP_FALSE );
}
} /* loadFromGameInfo */
static XP_Bool
stateToGameInfo( GameInfoState* state )
{
CEAppGlobals* globals = state->dlgHdr.globals;
CurGameInfo* gi = &globals->gameInfo;
HWND hDlg = state->dlgHdr.hDlg;
XP_Bool timerOn;
XP_Bool success = newg_store( state->newGameCtx, gi, XP_TRUE );
if ( success ) {
/* dictionary */ {
int sel;
sel = SendDlgItemMessage( hDlg, state->dictListId,
GETCURSEL(globals), 0, 0L );
if ( sel >= 0 ) {
WideCharToMultiByte( CP_ACP, 0, state->menuDicts[sel], -1,
state->newDictName,
sizeof(state->newDictName), NULL, NULL );
}
replaceStringIfDifferent( globals->mpool, &gi->dictName,
state->newDictName );
}
/* timer */
timerOn = ceGetChecked( hDlg, TIMER_CHECK );
gi->timerEnabled = timerOn;
if ( timerOn ) {
XP_UCHAR numBuf[10];
XP_U16 len = sizeof(numBuf);
ceGetDlgItemText( hDlg, TIMER_EDIT, numBuf, &len );
if ( len > 0 ) {
XP_U16 num = atoi( numBuf );
gi->gameSeconds = num * 60;
}
}
/* preferences */
if ( state->prefsChanged ) {
loadCurPrefsFromState( globals, &globals->appPrefs, gi,
&state->prefsPrefs );
}
}
return success;
} /* stateToGameInfo */
static void
raiseForHiddenPlayers( GameInfoState* state, XP_U16 nPlayers )
{
HWND hDlg = state->dlgHdr.hDlg;
XP_U16 ii;
XP_S16 moveY;
if ( nPlayers != state->prevNPlayers ) {
if ( !state->moveIds ) {
XP_S16 ids[32];
HWND child;
RECT rect;
XP_U16 playersBottom;
ceGetItemRect( hDlg, NAME_EDIT4, &rect );
playersBottom = rect.bottom;
ceGetItemRect( hDlg, NAME_EDIT3, &rect );
state->playersSpacing = playersBottom - rect.bottom;
for ( child = GetWindow( hDlg, GW_CHILD ), ii = 0;
!!child;
child = GetWindow( child, GW_HWNDNEXT ) ) {
XP_S16 resID = GetDlgCtrlID( child );
if ( resID > 0 ) {
ceGetItemRect( hDlg, resID, &rect );
if ( rect.top > playersBottom ) {
XP_ASSERT( ii < VSIZE(ids)-1 );
ids[ii] = resID;
++ii;
}
}
}
state->moveIds = XP_MALLOC( state->dlgHdr.globals->mpool,
sizeof(state->moveIds[0]) * ii );
XP_MEMCPY( state->moveIds, ids,
sizeof(state->moveIds[0]) * ii );
state->nMoveIds = ii;
}
moveY = state->playersSpacing * (nPlayers - state->prevNPlayers);
for ( ii = 0; ii < state->nMoveIds; ++ii ) {
ceMoveItem( hDlg, state->moveIds[ii], 0, moveY );
}
state->prevNPlayers = nPlayers;
#ifdef _WIN32_WCE
if ( IS_SMARTPHONE(state->dlgHdr.globals) ) {
SendMessage( hDlg, DM_RESETSCROLL, (WPARAM)FALSE, (LPARAM)TRUE );
}
#endif
}
}
static void
handlePrefsButton( HWND hDlg, CEAppGlobals* globals, GameInfoState* state )
{
XP_Bool colorsChanged;
if ( WrapPrefsDialog( hDlg, globals, &state->prefsPrefs,
state->isNewGame, &colorsChanged ) ) {
state->prefsChanged = XP_TRUE;
state->colorsChanged = colorsChanged;
/* nothing to do until user finally does confirm the parent dialog */
}
} /* handlePrefsButton */
#ifndef XWFEATURE_STANDALONE_ONLY
static void
callConnsDlg( GameInfoState* state )
{
if ( WrapConnsDlg( state->dlgHdr.hDlg, state->dlgHdr.globals,
&state->prefsPrefs.addrRec,
&state->prefsPrefs.addrRec, state->lastRole,
state->isNewGame ) ) {
state->addrChanged = XP_TRUE;
}
}
static void
handleConnOptionsButton( GameInfoState* state )
{
HWND hDlg = state->dlgHdr.hDlg;
CEAppGlobals* globals = state->dlgHdr.globals;
DeviceRole role;
NGValue value;
role = (DeviceRole)SendDlgItemMessage( hDlg, state->roleComboId,
GETCURSEL(globals), 0, 0L);
value.ng_role = role;
newg_attrChanged( state->newGameCtx, NG_ATTR_ROLE, value );
if ( state->lastRole != role ) {
state->lastRole = role;
if ( role != SERVER_STANDALONE ) {
callConnsDlg( state );
}
}
} /* handleConnOptionsButton */
#endif
static XP_U16
resIDForCol( XP_U16 player, NewGameColumn col )
{
XP_U16 resID = 0;
switch ( col ) {
#ifndef XWFEATURE_STANDALONE_ONLY
case NG_COL_REMOTE:
resID = REMOTE_CHECK1;
break;
#endif
case NG_COL_ROBOT:
resID = ROBOT_CHECK1;
break;
case NG_COL_NAME:
resID = NAME_EDIT1;
break;
case NG_COL_PASSWD:
resID = PASS_EDIT1;
break;
}
XP_ASSERT( resID != 0 );
return resID + ( player * NUM_COLS );
} /* resIDForCol */
static XP_U16
resIDForAttr( GameInfoState* state, NewGameAttr attr )
{
XP_U16 resID = 0;
switch( attr ) {
case NG_ATTR_NPLAYERS:
resID = state->nPlayersId;
break;
#ifndef XWFEATURE_STANDALONE_ONLY
case NG_ATTR_ROLE:
resID = state->roleComboId;
break;
case NG_ATTR_CANCONFIG:
resID = GIROLECONF_BUTTON;
break;
case NG_ATTR_REMHEADER:
resID = IDC_REMOTE_LABEL;
break;
#endif
case NG_ATTR_NPLAYHEADER:
resID = IDC_TOTAL_LABEL;
break;
case NG_ATTR_CANJUGGLE:
resID = GIJUGGLE_BUTTON;
break;
default:
break;
}
XP_ASSERT( resID != 0 );
return resID;
} /* resIDForAttr */
static void
doForNWEnable( HWND hDlg, XP_U16 resID, XP_TriEnable enable )
{
XP_Bool makeVisible = enable != TRI_ENAB_HIDDEN;
ceShowOrHide( hDlg, resID, makeVisible );
if ( makeVisible ) {
ceEnOrDisable( hDlg, resID, enable == TRI_ENAB_ENABLED );
}
} /* doForNWEnable */
static void
ceEnableColProc( void* closure, XP_U16 player, NewGameColumn col,
XP_TriEnable enable )
{
GameInfoState* state = (GameInfoState*)closure;
XP_U16 resID = resIDForCol( player, col );
doForNWEnable( state->dlgHdr.hDlg, resID, enable );
}
static void
ceEnableAttrProc( void* closure, NewGameAttr attr, XP_TriEnable enable )
{
GameInfoState* state = (GameInfoState*)closure;
XP_U16 resID = resIDForAttr( state, attr );
doForNWEnable( state->dlgHdr.hDlg, resID, enable );
} /* ceEnableAttrProc */
static void
ceGetColProc( void* closure, XP_U16 player, NewGameColumn col,
NgCpCallbk cpcb, const void* cpClosure )
{
NGValue value;
GameInfoState* state = (GameInfoState*)closure;
XP_U16 resID = resIDForCol( player, col );
XP_UCHAR txt[128];
XP_U16 len;
switch ( col ) {
#ifndef XWFEATURE_STANDALONE_ONLY
case NG_COL_REMOTE:
#endif
case NG_COL_ROBOT:
value.ng_bool = ceGetChecked( state->dlgHdr.hDlg, resID );
break;
case NG_COL_NAME:
case NG_COL_PASSWD:
len = sizeof(txt);
ceGetDlgItemText( state->dlgHdr.hDlg, resID, txt, &len );
value.ng_cp = &txt[0];
break;
default:
XP_ASSERT(0);
}
(*cpcb)( value, cpClosure );
} /* ceGetColProc */
static void
ceSetColProc( void* closure, XP_U16 player, NewGameColumn col,
const NGValue value )
{
GameInfoState* state = (GameInfoState*)closure;
XP_U16 resID = resIDForCol( player, col );
const XP_UCHAR* cp;
XP_UCHAR buf[32];
switch( col ) {
case NG_COL_PASSWD:
case NG_COL_NAME:
if ( NULL != value.ng_cp ) {
cp = value.ng_cp;
} else if ( col == NG_COL_NAME ) {
const XP_UCHAR* str = ceGetResString( state->dlgHdr.globals,
IDS_PLAYER_FORMAT );
snprintf( buf, sizeof(buf), str, player + 1 );
cp = buf;
} else {
cp = "";
}
ceSetDlgItemText( state->dlgHdr.hDlg, resID, cp );
break;
#ifndef XWFEATURE_STANDALONE_ONLY
case NG_COL_REMOTE:
#endif
case NG_COL_ROBOT:
ceSetChecked( state->dlgHdr.hDlg, resID, value.ng_bool );
break;
default:
XP_ASSERT(0);
}
} /* ceSetColProc */
static void
ceSetAttrProc(void* closure, NewGameAttr attr, const NGValue value )
{
GameInfoState* state = (GameInfoState*)closure;
XP_U16 resID = resIDForAttr( state, attr );
CEAppGlobals* globals = state->dlgHdr.globals;
switch ( attr ) {
case NG_ATTR_NPLAYERS:
SendDlgItemMessage( state->dlgHdr.hDlg, resID,
SETCURSEL(globals),
value.ng_u16 - 1, 0L );
raiseForHiddenPlayers( state, value.ng_u16 );
break;
#ifndef XWFEATURE_STANDALONE_ONLY
case NG_ATTR_ROLE:
SendDlgItemMessage( state->dlgHdr.hDlg, resID, SETCURSEL(globals),
value.ng_role, 0L );
break;
#endif
case NG_ATTR_NPLAYHEADER:
ceSetDlgItemText( state->dlgHdr.hDlg, resID, value.ng_cp );
break;
default:
break;
}
} /* ceSetAttrProc */
static XP_U16
playerFromID( XP_U16 id, XP_U16 base )
{
XP_U16 player;
player = (id - base) / NUM_COLS;
return player;
}
static void
handleColChecked( GameInfoState* state, XP_U16 id, XP_U16 base )
{
NGValue value;
XP_U16 player = playerFromID( id, base );
value.ng_bool = ceGetChecked( state->dlgHdr.hDlg, id );
newg_colChanged( state->newGameCtx, player );
}
/* It's too much work at this point to get the icon button looking good,
* e.g. properly greyed-out when disabled. So I'm sticking with the "J".
* Here's the code to start with if I get more ambitious. Remember: the
* GIJUGGLE_BUTTON needs to have the BS_OWNERDRAW attribute for this to work.
*/
#ifdef OWNERDRAW_JUGGLE
static void
ceDrawIconButton( CEAppGlobals* globals, DRAWITEMSTRUCT* dis )
{
HBITMAP bmp = LoadBitmap( globals->hInst,
MAKEINTRESOURCE(IDB_JUGGLEBUTTON) );
if ( !!bmp ) {
ceDrawBitmapInRect( dis->hDC, &dis->rcItem, bmp );
DeleteObject( bmp );
}
} /* ceDrawColorButton */
#endif
static void
checkUpdateCombo( GameInfoState* state, XP_U16 id )
{
HWND hDlg = state->dlgHdr.hDlg;
if ( id == state->nPlayersId ) {
if ( state->isNewGame ) { /* ignore if in info mode */
XP_S16 sel;
XP_U16 nPlayers;
NGValue value;
sel = SendDlgItemMessage( hDlg, id,
GETCURSEL(state->dlgHdr.globals), 0, 0L);
nPlayers = 1 + sel;
value.ng_u16 = nPlayers;
XP_ASSERT( !!state->newGameCtx );
newg_attrChanged( state->newGameCtx,
NG_ATTR_NPLAYERS, value );
raiseForHiddenPlayers( state, nPlayers );
}
} else if ( id == state->roleComboId ) {
XP_ASSERT( SERVER_STANDALONE == 0 );
handleConnOptionsButton( state );
}
} /* checkUpdateCombo */
LRESULT CALLBACK
GameInfo(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
CEAppGlobals* globals;
XP_U16 id;
GameInfoState* state;
LRESULT result = FALSE;
/* XP_LOGF( "%s: %s(%d)", __func__, messageToStr( message ), message ); */
if ( message == WM_INITDIALOG ) {
SetWindowLongPtr( hDlg, GWL_USERDATA, lParam );
state = (GameInfoState*)lParam;
globals = state->dlgHdr.globals;
state->nPlayersId = LB_IF_PPC(globals,IDC_NPLAYERSCOMBO);
#ifndef XWFEATURE_STANDALONE_ONLY
state->roleComboId = LB_IF_PPC(globals, IDC_ROLECOMBO);
#endif
state->dictListId = LB_IF_PPC(globals,IDC_DICTLIST);
state->prevNPlayers = MAX_NUM_PLAYERS;
ceDlgSetup( &state->dlgHdr, hDlg, DLG_STATE_TRAPBACK );
#ifndef XWFEATURE_STANDALONE_ONLY
ceDlgComboShowHide( &state->dlgHdr, IDC_ROLECOMBO );
#endif
ceDlgComboShowHide( &state->dlgHdr, IDC_NPLAYERSCOMBO );
ceDlgComboShowHide( &state->dlgHdr, IDC_DICTLIST );
state->newGameCtx = newg_make( MPPARM(globals->mpool)
state->isNewGame,
&globals->util,
ceEnableColProc,
ceEnableAttrProc,
ceGetColProc,
ceSetColProc,
ceSetAttrProc,
state );
loadFromGameInfo( state );
loadStateFromCurPrefs( globals, &globals->appPrefs, &globals->gameInfo,
&state->prefsPrefs );
if ( state->isNewGame ) {
(void)SetWindowText( hDlg, ceGetResStringL( globals,
IDS_NEW_GAME ) );
}
result = TRUE;
} else {
state = (GameInfoState*)GetWindowLongPtr( hDlg, GWL_USERDATA );
if ( !!state ) {
globals = state->dlgHdr.globals;
XP_ASSERT( hDlg == state->dlgHdr.hDlg );
result = ceDoDlgHandle( &state->dlgHdr, message, wParam, lParam );
if ( !result ) {
switch (message) {
#ifdef OWNERDRAW_JUGGLE
case WM_DRAWITEM: /* for BS_OWNERDRAW style */
ceDrawIconButton( globals, (DRAWITEMSTRUCT*)lParam );
result = TRUE;
break;
#endif
case WM_NOTIFY:
if ( !!state->newGameCtx ) {
checkUpdateCombo( state, LOWORD(wParam)-1 );
}
break;
case WM_COMMAND:
result = TRUE;
id = LOWORD(wParam);
if ( id == state->nPlayersId
#ifndef XWFEATURE_STANDALONE_ONLY
|| id == state->roleComboId
#endif
) {
if ( HIWORD(wParam) == CBN_SELCHANGE ) {
checkUpdateCombo( state, id );
}
} else {
switch( id ) {
case ROBOT_CHECK1:
case ROBOT_CHECK2:
case ROBOT_CHECK3:
case ROBOT_CHECK4:
handleColChecked( state, id, ROBOT_CHECK1 );
break;
#ifndef XWFEATURE_STANDALONE_ONLY
case REMOTE_CHECK1:
case REMOTE_CHECK2:
case REMOTE_CHECK3:
case REMOTE_CHECK4:
handleColChecked( state, id, REMOTE_CHECK1 );
break;
case IDC_ROLECOMBO:
case IDC_ROLECOMBO_PPC:
if ( HIWORD(wParam) == CBN_SELCHANGE ) {
/* If we've switched to a state where we'll be
connecting */
handleConnOptionsButton( state );
}
break;
case GIROLECONF_BUTTON:
callConnsDlg( state );
break;
#endif
case GIJUGGLE_BUTTON:
XP_ASSERT( state->isNewGame );
/* Juggle vs switch. On Win32, updates are
coalesced so you don't see anything on screen
if you change a field then change it back. In
terms of messages, all we see here is a
WM_CTLCOLOREDIT for each field being changed.
If I post a custom event here, it comes in
*before* the WM_CTLCOLOREDIT events. Short of
a timer, which starts a race with the user, I
see no way to get notified after the drawing's
done. So for now, we switch rather than
juggle: call juggle until something actually
happens. */
while ( !newg_juggle( state->newGameCtx ) ) {
}
break;
case OPTIONS_BUTTON:
handlePrefsButton( hDlg, globals, state );
break;
case IDOK:
if ( !stateToGameInfo( state ) ) {
break;
}
case IDCANCEL:
EndDialog(hDlg, id);
state->userCancelled = id == IDCANCEL;
cleanupGameInfoState( state );
newg_destroy( state->newGameCtx );
state->newGameCtx = NULL;
}
break;
default:
result = FALSE;
}
}
}
}
}
return result;
} /* GameInfo */