mirror of
git://xwords.git.sourceforge.net/gitroot/xwords/xwords
synced 2025-01-29 08:34:37 +01:00
Merge branch 'android_branch' into send_in_background
This commit is contained in:
commit
82615f409b
17 changed files with 332 additions and 128 deletions
|
@ -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);
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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 -->
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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, ¬ifyInfo,
|
||||
(MovePrintFuncPre)NULL, (MovePrintFuncPost)NULL,
|
||||
NULL );
|
||||
(XWStreamCtxt*)NULL, ni, (MovePrintFuncPre)NULL,
|
||||
(MovePrintFuncPost)NULL, NULL );
|
||||
}
|
||||
stack_destroy( tmpStack );
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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))
|
||||
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Reference in a new issue