Merge branch 'android_branch' into send_in_background

This commit is contained in:
eehouse@eehouse.org 2011-10-04 06:49:19 -07:00 committed by Andy2
commit 82615f409b
17 changed files with 332 additions and 128 deletions

View file

@ -215,6 +215,24 @@ and_util_turnChanged(XW_UtilCtxt* uc)
}
#endif
static void
and_util_informMove( XW_UtilCtxt* uc, XWStreamCtxt* expl,
XWStreamCtxt* words, XP_U16 wordCount )
{
UTIL_CBK_HEADER( "informMove", "(Ljava/lang/String;Ljava/lang/String;)V" );
jstring jexpl = streamToJString( MPPARM(util->util.mpool) env, expl );
jstring jwords = !!words ?
streamToJString( MPPARM(util->util.mpool) env, words ) : NULL;
(*env)->CallVoidMethod( env, util->jutil, mid, jexpl, jwords );
(*env)->DeleteLocalRef( env, jexpl );
if ( !!jwords ) {
(*env)->DeleteLocalRef( env, jwords );
} else {
XP_ASSERT( 0 == wordCount );
}
UTIL_CBK_TAIL();
}
static void
and_util_notifyGameOver( XW_UtilCtxt* uc )
{
@ -476,6 +494,7 @@ makeUtil( MPFORMAL JNIEnv** envp, jobject jutil, CurGameInfo* gi,
#ifdef XWFEATURE_TURNCHANGENOTIFY
SET_PROC( turnChanged);
#endif
SET_PROC(informMove);
SET_PROC(notifyGameOver);
SET_PROC(hiliteCell);
SET_PROC(engineProgressCallback);

View file

@ -30,6 +30,8 @@
<item android:title="@string/board_submenu_game">
<menu>
<item android:id="@+id/board_menu_lookup"
android:title="@string/button_lookup" />
<item android:id="@+id/board_menu_game_counts"
android:title="@string/board_menu_game_counts" />
<item android:id="@+id/board_menu_game_left"

View file

@ -175,22 +175,22 @@
<item>en</item>
<item>fr</item>
<item>de</item>
<item></item>
<item></item>
<item>tr</item>
<item>ar</item>
<item>es</item>
<item></item>
<item></item>
<item></item>
<item>sv</item>
<item>pl</item>
<item>da</item>
<item>it</item>
<item></item>
<item>nl</item>
<item>ca</item>
<item>pt</item>
<item></item>
<item>ru</item>
<item></item>
<item></item>
<item></item>
<item></item>
<item></item>
<item></item>
<item>cs</item>
<item>el</item>
<item>sk</item>
</string-array>
<!-- Triples of Name, supported codes, and URL format string -->

View file

@ -1765,13 +1765,14 @@
<string name="changes_button">Recent changes</string>
<string name="button_lookup">Lookup word</string>
<string name="title_lookup">Tap to lookup</string>
<string name="button_lookup">Look up words</string>
<string name="button_lookupf">Look up %s</string>
<string name="title_lookup">Tap to look up</string>
<string name="button_done">Done</string>
<string name="site_spinner_label">Pick a site</string>
<string name="site_spinner_prompt">Pick a site</string>
<string name="word_list_label">Tap word to search</string>
<string name="pick_url_titlef">Look up %s at</string>
</resources>

View file

@ -40,6 +40,7 @@ import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.DialogInterface.OnDismissListener;
import android.content.DialogInterface.OnCancelListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
@ -80,6 +81,7 @@ public class BoardActivity extends XWActivity
private static final int DLG_INVITE = DLG_OKONLY + 9;
private static final int DLG_SCORES_BLK = DLG_OKONLY + 10;
private static final int DLG_WORDPICK = DLG_OKONLY + 11;
private static final int DLG_URLPICK = DLG_OKONLY + 12;
private static final int CHAT_REQUEST = 1;
private static final int SCREEN_ON_TIME = 10 * 60 * 1000; // 10 mins
@ -104,6 +106,7 @@ public class BoardActivity extends XWActivity
private static final String ROOM = "ROOM";
private static final String TOASTSTR = "TOASTSTR";
private static final String WORDS = "WORDS";
private static final String LOOKUPITEM = "LOOKUPITEM";
private BoardView m_view;
private int m_jniGamePtr;
@ -145,6 +148,8 @@ public class BoardActivity extends XWActivity
private String m_room;
private String m_toastStr;
private String[] m_words;
private String[] m_wordsWaiting;
private String m_word;
private String[] m_langCodes;
private String[] m_lookupUrls;
private String[] m_lookupNames;
@ -176,6 +181,7 @@ public class BoardActivity extends XWActivity
Dialog dialog = super.onCreateDialog( id );
if ( null == dialog ) {
DialogInterface.OnClickListener lstnr;
DialogInterface.OnClickListener doneLstnr;
AlertDialog.Builder ab;
switch ( id ) {
@ -234,6 +240,8 @@ public class BoardActivity extends XWActivity
public void onClick( DialogInterface dialog,
int whichButton ) {
m_resultCode = 1;
m_words = null; // in case it's DLG_SCORES_BLK
m_wordsWaiting = null;
}
};
ab.setPositiveButton( QUERY_REQUEST_BLK == id ?
@ -248,13 +256,24 @@ public class BoardActivity extends XWActivity
};
ab.setNegativeButton( R.string.button_no, lstnr );
} else if ( DLG_SCORES_BLK == id ) {
lstnr = new DialogInterface.OnClickListener() {
public void onClick( DialogInterface dialog,
int whichButton ) {
m_jniThread.handle( JNICmd.CMD_WORDS );
}
};
ab.setNegativeButton( R.string.button_lookup, lstnr );
if ( null != m_wordsWaiting && m_wordsWaiting.length > 0 ) {
String buttonTxt;
if ( m_wordsWaiting.length == 1 ) {
buttonTxt = Utils.format( this,
R.string.button_lookupf,
m_wordsWaiting[0] );
} else {
buttonTxt = getString( R.string.button_lookup );
}
lstnr = new DialogInterface.OnClickListener() {
public void onClick( DialogInterface dialog,
int whichButton ) {
m_words = m_wordsWaiting;
lookupWord();
}
};
ab.setNegativeButton( buttonTxt, lstnr );
}
}
dialog = ab.create();
@ -326,13 +345,66 @@ public class BoardActivity extends XWActivity
}
break;
case DLG_WORDPICK:
initLookup();
lstnr = new DialogInterface.OnClickListener() {
public void onClick( DialogInterface dialog,
int item ) {
lookupWord( m_words[item] );
}
};
doneLstnr = new DialogInterface.OnClickListener() {
public void onClick( DialogInterface dlg,
int item ) {
wordPickDone();
}
};
dialog = new AlertDialog.Builder( this )
.setTitle( R.string.title_lookup )
.setView( buildLookupDlg() )
.setNegativeButton( R.string.button_done, null )
.setItems( m_words, lstnr )
.setNegativeButton( R.string.button_done, doneLstnr )
.setOnCancelListener( new OnCancelListener() {
public void onCancel( DialogInterface dialog ) {
wordPickDone();
}
} )
.create();
Utils.setRemoveOnDismiss( this, dialog, id );
break;
case DLG_URLPICK:
DialogInterface.OnClickListener itemLstnr =
new DialogInterface.OnClickListener() {
public void onClick( DialogInterface dialog,
int item ) {
lookupWord( m_word, m_lookupUrls[item] );
post( new Runnable() {
public void run() {
showDialog( DLG_URLPICK );
}
} );
}
};
doneLstnr = new DialogInterface.OnClickListener() {
public void onClick( DialogInterface dialog,
int item ) {
urlPickDone();
}
};
String fmt = getString( R.string.pick_url_titlef );
String title = String.format( fmt, m_word );
dialog = new AlertDialog.Builder( this )
.setTitle( title )
.setItems( m_lookupNames, itemLstnr )
.setNegativeButton( R.string.button_done, doneLstnr )
.setOnCancelListener( new OnCancelListener() {
public void onCancel( DialogInterface dialog ) {
urlPickDone();
}
} )
.create();
Utils.setRemoveOnDismiss( this, dialog, id );
break;
default:
// just drop it; super.onCreateDialog likely failed
break;
@ -420,7 +492,8 @@ public class BoardActivity extends XWActivity
outState.putString( DLG_BYTES, m_dlgBytes );
outState.putString( ROOM, m_room );
outState.putString( TOASTSTR, m_toastStr );
outState.putStringArray( WORDS, m_words );
outState.putStringArray( WORDS, m_wordsWaiting );
outState.putString( LOOKUPITEM, m_word );
}
private void getBundledData( Bundle bundle )
@ -431,7 +504,8 @@ public class BoardActivity extends XWActivity
m_dlgBytes = bundle.getString( DLG_BYTES );
m_room = bundle.getString( ROOM );
m_toastStr = bundle.getString( TOASTSTR );
m_words = bundle.getStringArray( WORDS );
m_wordsWaiting = bundle.getStringArray( WORDS );
m_word = bundle.getString( LOOKUPITEM );
}
}
@ -463,6 +537,8 @@ public class BoardActivity extends XWActivity
// in case of change...
setBackgroundColor();
setKeepScreenOn();
} else {
lookupWord();
}
}
}
@ -566,6 +642,9 @@ public class BoardActivity extends XWActivity
case R.id.board_menu_values:
cmd = JNIThread.JNICmd.CMD_VALUES;
break;
case R.id.board_menu_lookup:
m_jniThread.handle( JNICmd.CMD_WORDS, 10000 );
break;
case R.id.board_menu_game_counts:
m_jniThread.handle( JNIThread.JNICmd.CMD_COUNTS_VALUES,
@ -1063,12 +1142,10 @@ public class BoardActivity extends XWActivity
// if the player after this one is also a robot and we
// don't block then a second dialog will replace this one.
// So block. Yuck.
case UtilCtxt.QUERY_ROBOT_MOVE:
case UtilCtxt.QUERY_ROBOT_TRADE:
m_dlgBytes = query;
m_dlgTitle = R.string.info_title;
waitBlockingDialog( QUERY_ROBOT_MOVE == id ?
DLG_SCORES_BLK : QUERY_INFORM_BLK, 0 );
waitBlockingDialog( QUERY_INFORM_BLK, 0 );
result = true;
break;
@ -1146,6 +1223,15 @@ public class BoardActivity extends XWActivity
}
} // userError
public void informMove( String expl, String words )
{
m_dlgBytes = expl;
m_dlgTitle = R.string.info_title;
m_wordsWaiting = wordsToMWords( words );
Assert.assertNull( m_words );
waitBlockingDialog( DLG_SCORES_BLK, 0 );
}
public void notifyGameOver()
{
m_jniThread.handle( JNIThread.JNICmd.CMD_POST_OVER );
@ -1267,15 +1353,8 @@ public class BoardActivity extends XWActivity
}
break;
case JNIThread.GOT_WORDS:
m_words =
TextUtils.split( (String)msg.obj, "\n" );
if ( 0 == m_words.length ) {
// drop it
// } else if ( 1 == m_words.length ) {
// lookupWord( m_words[0] );
} else {
showDialog( DLG_WORDPICK );
}
m_words = wordsToMWords( (String)msg.obj );
lookupWord();
break;
}
}
@ -1549,59 +1628,46 @@ public class BoardActivity extends XWActivity
}
}
private View buildLookupDlg()
private void lookupWord()
{
initLookup();
if ( null == m_words || 0 == m_words.length ) {
// drop it
} else if ( null != m_word ) {
lookupWord( m_word );
} else {
showDialog( DLG_WORDPICK );
}
}
LinearLayout layout =
(LinearLayout)Utils.inflate( this, R.layout.wordlist_view );
final Spinner spinner =
(Spinner)layout.findViewById( R.id.site_spinner );
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_spinner_item,
m_lookupNames );
spinner.setAdapter( adapter );
ListView list = (ListView)layout.findViewById( R.id.words );
adapter = new ArrayAdapter<String>( this,
//android.R.layout.select_dialog_item,
// android.R.layout.simple_list_item_1,
android.R.layout.select_dialog_item,
m_words ) ;
list.setAdapter( adapter );
OnItemClickListener oicl = new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent,
View view,
int position, long id ) {
int urlPos = spinner.getSelectedItemPosition();
lookupWord( m_words[position], m_lookupUrls[urlPos] );
}
};
list.setOnItemClickListener( oicl );
return layout;
private void lookupWord( String word )
{
m_word = word;
if ( 1 == m_lookupUrls.length ) {
lookupWord( word, m_lookupUrls[0] );
} else {
showDialog( DLG_URLPICK );
}
}
private void lookupWord( String word, String fmt )
{
String dict_url = String.format( fmt, curLangCode(), word );
Uri uri = Uri.parse( dict_url );
Intent intent = new Intent( Intent.ACTION_VIEW, uri );
intent.setFlags( Intent.FLAG_ACTIVITY_NEW_TASK );
if ( false ) {
Utils.logf( "skipping lookupWord(%s)", word );
} else {
String langCode = m_langCodes[m_gi.dictLang];
String dict_url = String.format( fmt, langCode, word );
Uri uri = Uri.parse( dict_url );
Intent intent = new Intent( Intent.ACTION_VIEW, uri );
intent.setFlags( Intent.FLAG_ACTIVITY_NEW_TASK );
try {
startActivity( intent );
} catch ( android.content.ActivityNotFoundException anfe ) {
Utils.logf( "%s", anfe.toString() );
try {
startActivity( intent );
} catch ( android.content.ActivityNotFoundException anfe ) {
Utils.logf( "%s", anfe.toString() );
}
}
}
private String curLangCode()
{
initLookup();
return m_langCodes[m_gi.dictLang];
}
} // lookupWord
private void initLookup()
{
@ -1624,6 +1690,32 @@ public class BoardActivity extends XWActivity
m_lookupNames = tmpNames.toArray( new String[tmpNames.size()] );
m_lookupUrls = tmpUrls.toArray( new String[tmpUrls.size()] );
}
} // initLookup
private void urlPickDone() {
m_word = null;
if ( null != m_words && 1 >= m_words.length ) {
m_words = null;
m_wordsWaiting = null;
}
}
private void wordPickDone() {
m_words = null;
m_wordsWaiting = null;
}
private String[] wordsToMWords( String words )
{
String[] tmp = TextUtils.split( words, "\n" );
String[] wordsArray = new String[tmp.length];
for ( int ii = 0, jj = tmp.length; ii < tmp.length; ++ii, --jj ) {
wordsArray[ii] = tmp[jj-1];
}
if ( 1 == wordsArray.length ) {
m_word = wordsArray[0];
}
return wordsArray;
}
} // class BoardActivity

View file

@ -212,4 +212,10 @@ public class Utils {
return 0;
}
}
public static String format( Context context, int id, Object... args )
{
String fmt = context.getString( id );
return String.format( fmt, args );
}
}

View file

@ -493,7 +493,9 @@ public class JNIThread extends Thread {
break;
case CMD_WORDS:
String words = XwJNI.model_getWordsPlayed( m_jniGamePtr, 1 );
int nMoves = ((Integer)args[0]).intValue();
String words = XwJNI.model_getWordsPlayed( m_jniGamePtr,
nMoves );
Message.obtain( m_handler, GOT_WORDS, words ).sendToTarget();
break;

View file

@ -76,8 +76,7 @@ public interface UtilCtxt {
static final int QUERY_COMMIT_TURN = 0;
static final int QUERY_COMMIT_TRADE = 1;
static final int QUERY_ROBOT_MOVE = 2;
static final int QUERY_ROBOT_TRADE = 3;
static final int QUERY_ROBOT_TRADE = 2;
boolean userQuery( int id, String query );
@ -101,6 +100,8 @@ public interface UtilCtxt {
static final int ERR_RELAY_BASE = 16;
void userError( int id );
void informMove( String expl, String words );
void notifyGameOver();
// Don't need this unless we have a scroll thumb to indicate position
//void yOffsetChange( int maxOffset, int oldOffset, int newOffset );

View file

@ -185,6 +185,11 @@ public class UtilCtxtImpl implements UtilCtxt {
subclassOverride( "userError" );
}
public void informMove( String expl, String words )
{
subclassOverride( "informMove" );
}
// Probably want to cache the fact that the game over notification
// showed up and then display it next time game's opened.
public void notifyGameOver()

View file

@ -31,6 +31,7 @@
extern "C" {
#endif
#define STREAM_SAVE_PREVWORDS 0x11
#define STREAM_VERS_SERVER_SAVES_TOSHOW 0x10
#define STREAM_VERS_PLAYERDICTS 0x0F
#define STREAM_SAVE_PREVMOVE 0x0E /* server saves prev move explanation */
@ -52,7 +53,7 @@ extern "C" {
#define STREAM_VERS_41B4 0x02
#define STREAM_VERS_405 0x01
#define CUR_STREAM_VERS STREAM_VERS_SERVER_SAVES_TOSHOW
#define CUR_STREAM_VERS STREAM_SAVE_PREVWORDS
typedef struct LocalPlayer {
XP_UCHAR* name;

View file

@ -75,7 +75,7 @@ static void loadPlayerCtxt( XWStreamCtxt* stream, XP_U16 version,
PlayerCtxt* pc );
static void writePlayerCtxt( XWStreamCtxt* stream, PlayerCtxt* pc );
static XP_U16 model_getRecentPassCount( ModelCtxt* model );
static XP_Bool recordWord( const XP_UCHAR* word, XP_Bool isLegal, void* clsur );
/*****************************************************************************
*
@ -91,6 +91,8 @@ model_make( MPFORMAL DictionaryCtxt* dict,
MPASSIGN(result->vol.mpool, mpool);
result->vol.util = util;
result->vol.wni.proc = recordWord;
result->vol.wni.closure = &result->vol.rwi;
model_init( result, nCols, nRows );
@ -216,7 +218,7 @@ model_writeToTextStream( const ModelCtxt* model, XWStreamCtxt* stream )
void
model_init( ModelCtxt* model, XP_U16 nCols, XP_U16 nRows )
{
ModelVolatiles vol = model->vol;
ModelVolatiles vol = model->vol; /* save vol so we don't wipe it out */
XP_ASSERT( model != NULL );
XP_MEMSET( model, 0, sizeof( *model ) );
@ -2032,11 +2034,6 @@ model_recentPassCountOk( ModelCtxt* model )
return count < okCount;
}
typedef struct _RecordWordsInfo {
XWStreamCtxt* stream;
XP_U16 nWords;
} RecordWordsInfo;
static XP_Bool
recordWord( const XP_UCHAR* word, XP_Bool isLegal, void* closure )
{
@ -2048,9 +2045,23 @@ recordWord( const XP_UCHAR* word, XP_Bool isLegal, void* closure )
stream_putU8( stream, '\n' );
}
stream_catString( stream, word );
if ( NULL != info->nWordsP ) {
*info->nWordsP = info->nWords;
}
return XP_TRUE;
}
WordNotifierInfo*
model_initWordCounter( ModelCtxt* model, XWStreamCtxt* stream, XP_U16* nWords )
{
XP_ASSERT( model->vol.wni.proc == recordWord );
XP_ASSERT( model->vol.wni.closure == &model->vol.rwi );
model->vol.rwi.stream = stream;
model->vol.rwi.nWordsP = nWords;
model->vol.rwi.nWords = 0;
return &model->vol.wni;
}
void
model_getWordsPlayed( ModelCtxt* model, XP_U16 nTurns, XWStreamCtxt* stream )
{
@ -2066,19 +2077,14 @@ model_getWordsPlayed( ModelCtxt* model, XP_U16 nTurns, XWStreamCtxt* stream )
}
if ( model_undoLatestMoves( model, NULL, nTurns, NULL, NULL ) ) {
RecordWordsInfo info = { .stream = stream, .nWords = 0 };
WordNotifierInfo notifyInfo = { .proc = recordWord,
.closure = &info,
};
WordNotifierInfo* ni = model_initWordCounter( model, stream, NULL );
/* Now push the undone moves back into the model one at a time.
recordWord() will add each played word to the stream as it's
scored */
buildModelFromStack( model, tmpStack, XP_TRUE,
nEntries - nTurns + nPlayers,/* skip assignments */
(XWStreamCtxt*)NULL, &notifyInfo,
(MovePrintFuncPre)NULL, (MovePrintFuncPost)NULL,
NULL );
(XWStreamCtxt*)NULL, ni, (MovePrintFuncPre)NULL,
(MovePrintFuncPost)NULL, NULL );
}
stack_destroy( tmpStack );
}

View file

@ -271,6 +271,10 @@ XP_U16 figureMoveScore( const ModelCtxt* model, XP_U16 turn, MoveInfo* mvInfo,
EngineCtxt* engine, XWStreamCtxt* stream,
WordNotifierInfo* notifyInfo );
/* tap into internal WordNotifierInfo */
WordNotifierInfo* model_initWordCounter( ModelCtxt* model, XWStreamCtxt* stream,
XP_U16* nWords );
/********************* persistence ********************/
#ifdef INCLUDE_IO_SUPPORT
void model_load( ModelCtxt* model, XP_Stream* inStream );

View file

@ -43,6 +43,12 @@ typedef struct PlayerCtxt {
PendingTile pendingTiles[MAX_TRAY_TILES];
} PlayerCtxt;
typedef struct _RecordWordsInfo {
XWStreamCtxt* stream;
XP_U16* nWordsP;
XP_U16 nWords;
} RecordWordsInfo;
typedef struct ModelVolatiles {
XW_UtilCtxt* util;
struct CurGameInfo* gi;
@ -55,6 +61,8 @@ typedef struct ModelVolatiles {
void* trayListenerData;
DictListener dictListenerFunc;
void* dictListenerData;
RecordWordsInfo rwi;
WordNotifierInfo wni;
XP_U16 nTilesOnBoard;
MPSLOT
} ModelVolatiles;

View file

@ -92,6 +92,8 @@ typedef struct ServerNonvolatiles {
RemoteAddress addresses[MAX_NUM_PLAYERS];
XWStreamCtxt* prevMoveStream; /* save it to print later */
XWStreamCtxt* prevWordsStream;
XP_U16 prevWordCount;
} ServerNonvolatiles;
struct ServerCtxt {
@ -174,7 +176,8 @@ getStateStr( XW_State st )
}
#endif
#ifdef DEBUG
#if 0
//def DEBUG
static void
logNewState( XW_State old, XW_State newst )
{
@ -304,6 +307,26 @@ putNV( XWStreamCtxt* stream, ServerNonvolatiles* nv, XP_U16 nPlayers )
}
} /* putNV */
static XWStreamCtxt*
readStreamIf( ServerCtxt* server, XWStreamCtxt* in )
{
XWStreamCtxt* result = NULL;
XP_U16 len = stream_getU16( in );
if ( 0 < len ) {
result = mkServerStream( server );
stream_copyFromStream( in, result, len );
}
return result;
}
static void
writeStreamIf( XWStreamCtxt* dest, XWStreamCtxt* src )
{
XP_U16 len = !!src ? stream_getSize( src ) : 0;
stream_putU16( dest, len );
stream_copyFromStream( dest, src, len );
}
ServerCtxt*
server_makeFromStream( MPFORMAL XWStreamCtxt* stream, ModelCtxt* model,
CommsCtxt* comms, XW_UtilCtxt* util, XP_U16 nPlayers )
@ -339,15 +362,11 @@ server_makeFromStream( MPFORMAL XWStreamCtxt* stream, ModelCtxt* model,
}
if ( version >= STREAM_SAVE_PREVMOVE ) {
XP_U16 nBytes = stream_getU16( stream );
if ( nBytes > 0 ) {
XP_ASSERT( !server->nv.prevMoveStream );
server->nv.prevMoveStream = mkServerStream( server );
stream_copyFromStream( server->nv.prevMoveStream,
stream, nBytes );
}
server->nv.prevMoveStream = readStreamIf( server, stream );
}
if ( version >= STREAM_SAVE_PREVWORDS ) {
server->nv.prevWordsStream = readStreamIf( server, stream );
}
XP_ASSERT( stream_getU32( stream ) == sEND );
return server;
@ -358,7 +377,6 @@ server_writeToStream( ServerCtxt* server, XWStreamCtxt* stream )
{
XP_U16 i;
XP_U16 nPlayers = server->vol.gi->nPlayers;
XP_U16 nBytes;
putNV( stream, &server->nv, nPlayers );
@ -384,12 +402,8 @@ server_writeToStream( ServerCtxt* server, XWStreamCtxt* stream )
stream_putBits( stream, 2, 0 );
#endif
nBytes = !!server->nv.prevMoveStream ?
stream_getSize( server->nv.prevMoveStream ) : 0;
stream_putU16( stream, nBytes );
if ( nBytes > 0 ) {
stream_copyFromStream( stream, server->nv.prevMoveStream, nBytes );
}
writeStreamIf( stream, server->nv.prevMoveStream );
writeStreamIf( stream, server->nv.prevWordsStream );
#ifdef DEBUG
stream_putU32( stream, sEND );
@ -417,6 +431,9 @@ cleanupServer( ServerCtxt* server )
if ( !!server->nv.prevMoveStream ) {
stream_destroy( server->nv.prevMoveStream );
}
if ( !!server->nv.prevWordsStream ) {
stream_destroy( server->nv.prevWordsStream );
}
XP_MEMSET( &server->nv, 0, sizeof(server->nv) );
} /* cleanupServer */
@ -739,9 +756,14 @@ makeRobotMove( ServerCtxt* server )
model_makeTurnFromMoveInfo( model, turn, &newMove );
if ( !!stream ) {
(void)model_checkMoveLegal( model, turn, stream, NULL );
XWStreamCtxt* wordsStream = mkServerStream( server );
WordNotifierInfo* ni =
model_initWordCounter( model, wordsStream,
&server->nv.prevWordCount );
(void)model_checkMoveLegal( model, turn, stream, ni );
XP_ASSERT( !server->nv.prevMoveStream );
server->nv.prevMoveStream = stream;
server->nv.prevWordsStream = wordsStream;
}
result = server_commitMove( server );
} else {
@ -848,8 +870,11 @@ showPrevScore( ServerCtxt* server )
stream_destroy( prevStream );
}
(void)util_userQuery( util, QUERY_ROBOT_MOVE, stream );
util_informMove( util, stream, server->nv.prevWordsStream,
server->nv.prevWordCount );
stream_destroy( stream );
stream_destroy( server->nv.prevWordsStream );
server->nv.prevWordsStream = NULL;
}
SETSTATE( server, server->nv.stateAfterShow );
} /* showPrevScore */
@ -1853,14 +1878,17 @@ makeTradeReportIf( ServerCtxt* server, const TrayTileSet* tradedTiles )
} /* makeTradeReportIf */
static XWStreamCtxt*
makeMoveReportIf( ServerCtxt* server )
makeMoveReportIf( ServerCtxt* server, XWStreamCtxt** wordsStream )
{
XWStreamCtxt* stream = NULL;
if ( server->nv.showRobotScores ) {
ModelCtxt* model = server->vol.model;
stream = mkServerStream( server );
(void)model_checkMoveLegal( server->vol.model,
server->nv.currentTurn, stream,
NULL );
*wordsStream = mkServerStream( server );
WordNotifierInfo* ni =
model_initWordCounter( model, *wordsStream,
&server->nv.prevWordCount );
(void)model_checkMoveLegal( model, server->nv.currentTurn, stream, ni );
}
return stream;
} /* makeMoveReportIf */
@ -1885,6 +1913,7 @@ reflectMoveAndInform( ServerCtxt* server, XWStreamCtxt* stream )
XP_U16 sourceClientIndex =
getIndexForDevice( server, stream_getAddress( stream ) );
XWStreamCtxt* mvStream = NULL;
XWStreamCtxt* wordsStream = NULL;
XP_ASSERT( gi->serverRole == SERVER_ISSERVER );
@ -1917,7 +1946,7 @@ reflectMoveAndInform( ServerCtxt* server, XWStreamCtxt* stream )
(TrayTileSet*)NULL, sourceClientIndex );
server->vol.showPrevMove = XP_TRUE;
mvStream = makeMoveReportIf( server );
mvStream = makeMoveReportIf( server, &wordsStream );
model_commitTurn( model, whoMoved, &newTiles );
resetEngines( server );
@ -1940,8 +1969,9 @@ reflectMoveAndInform( ServerCtxt* server, XWStreamCtxt* stream )
if ( !!mvStream ) {
XP_ASSERT( !server->nv.prevMoveStream );
server->nv.prevMoveStream = mvStream;
XP_ASSERT( !server->nv.prevWordsStream );
server->nv.prevWordsStream = wordsStream;
}
} else {
/* The client from which the move came still needs to be told. But we
can't send a message now since we're burried in a message handler.
@ -1970,6 +2000,7 @@ reflectMove( ServerCtxt* server, XWStreamCtxt* stream )
TrayTileSet tradedTiles;
ModelCtxt* model = server->vol.model;
XWStreamCtxt* mvStream = NULL;
XWStreamCtxt* wordsStream = NULL;
moveOk = XWSTATE_INTURN == server->nv.gameState;
XP_ASSERT( moveOk ); /* message permanently lost if dropped here! */
@ -1985,13 +2016,15 @@ reflectMove( ServerCtxt* server, XWStreamCtxt* stream )
mvStream = makeTradeReportIf( server, &tradedTiles );
} else {
server->vol.showPrevMove = XP_TRUE;
mvStream = makeMoveReportIf( server );
mvStream = makeMoveReportIf( server, &wordsStream );
model_commitTurn( model, whoMoved, &newTiles );
}
if ( !!mvStream ) {
XP_ASSERT( !server->nv.prevMoveStream );
server->nv.prevMoveStream = mvStream;
XP_ASSERT( !server->nv.prevWordsStream );
server->nv.prevWordsStream = wordsStream;
}
resetEngines( server );

View file

@ -66,7 +66,6 @@ typedef enum {
typedef enum {
QUERY_COMMIT_TURN, /* 0 means cancel; 1 means commit */
QUERY_COMMIT_TRADE,
QUERY_ROBOT_MOVE,
QUERY_ROBOT_TRADE,
QUERY_LAST_COMMON
@ -131,6 +130,8 @@ typedef struct UtilVtable {
#ifdef XWFEATURE_TURNCHANGENOTIFY
void (*m_util_turnChanged)(XW_UtilCtxt* uc);
#endif
void (*m_util_informMove)( XW_UtilCtxt* uc, XWStreamCtxt* expl,
XWStreamCtxt* words, XP_U16 wordCount );
void (*m_util_notifyGameOver)( XW_UtilCtxt* uc );
XP_Bool (*m_util_hiliteCell)( XW_UtilCtxt* uc, XP_U16 col, XP_U16 row );
@ -227,6 +228,8 @@ struct XW_UtilCtxt {
# define util_turnChanged( uc )
#endif
#define util_informMove(uc,e,w,wc) \
(uc)->vtable->m_util_informMove( (uc),(e),(w),(wc) )
#define util_notifyGameOver( uc ) \
(uc)->vtable->m_util_notifyGameOver((uc))

View file

@ -283,7 +283,6 @@ curses_util_userQuery( XW_UtilCtxt* uc, UtilQueryID id, XWStreamCtxt* stream )
answers[numAnswers++] = "Cancel";
answers[numAnswers++] = "Ok";
break;
case QUERY_ROBOT_MOVE:
case QUERY_ROBOT_TRADE:
question = strFromStream( stream );
freeMe = XP_TRUE;
@ -333,6 +332,17 @@ cursesShowFinalScores( CursesAppGlobals* globals )
stream_destroy( stream );
} /* cursesShowFinalScores */
static void
curses_util_informMove( XW_UtilCtxt* uc, XWStreamCtxt* expl,
XWStreamCtxt* XP_UNUSED(words),
XP_U16 XP_UNUSED(wordCount) )
{
CursesAppGlobals* globals = (CursesAppGlobals*)uc->closure;
char* question = strFromStream( expl );
(void)cursesask( globals, question, 1, "Ok" );
free( question );
}
static void
curses_util_notifyGameOver( XW_UtilCtxt* uc )
{
@ -1466,6 +1476,7 @@ setupCursesUtilCallbacks( CursesAppGlobals* globals, XW_UtilCtxt* util )
util->vtable->m_util_userQuery = curses_util_userQuery;
util->vtable->m_util_userPickTile = curses_util_userPickTile;
util->vtable->m_util_trayHiddenChange = curses_util_trayHiddenChange;
util->vtable->m_util_informMove = curses_util_informMove;
util->vtable->m_util_notifyGameOver = curses_util_notifyGameOver;
util->vtable->m_util_hiliteCell = curses_util_hiliteCell;
util->vtable->m_util_engineProgressCallback =

View file

@ -1386,6 +1386,16 @@ gtkShowFinalScores( const CommonGlobals* cGlobals )
free( text );
} /* gtkShowFinalScores */
static void
gtk_util_informMove( XW_UtilCtxt* XP_UNUSED(uc), XWStreamCtxt* XP_UNUSED(expl),
XWStreamCtxt* words, XP_U16 wordCount )
{
XP_LOGF( "%s(wordCount=%d)", __func__, wordCount );
char* question = strFromStream( words/*expl*/ );
(void)gtkask( question, GTK_BUTTONS_OK );
free( question );
}
static void
gtk_util_notifyGameOver( XW_UtilCtxt* uc )
{
@ -1763,7 +1773,6 @@ gtk_util_userQuery( XW_UtilCtxt* XP_UNUSED(uc), UtilQueryID id,
case QUERY_COMMIT_TRADE:
question = "Are you sure you want to trade the selected tiles?";
break;
case QUERY_ROBOT_MOVE:
case QUERY_ROBOT_TRADE:
question = strFromStream( stream );
freeMe = XP_TRUE;
@ -1927,6 +1936,7 @@ setupGtkUtilCallbacks( GtkAppGlobals* globals, XW_UtilCtxt* util )
util->vtable->m_util_askPassword = gtk_util_askPassword;
util->vtable->m_util_trayHiddenChange = gtk_util_trayHiddenChange;
util->vtable->m_util_yOffsetChange = gtk_util_yOffsetChange;
util->vtable->m_util_informMove = gtk_util_informMove;
util->vtable->m_util_notifyGameOver = gtk_util_notifyGameOver;
util->vtable->m_util_hiliteCell = gtk_util_hiliteCell;
util->vtable->m_util_altKeyDown = gtk_util_altKeyDown;