diff --git a/xwords4/android/XWords4/AndroidManifest.xml b/xwords4/android/XWords4/AndroidManifest.xml index 74a1e6aac..61ae73545 100644 --- a/xwords4/android/XWords4/AndroidManifest.xml +++ b/xwords4/android/XWords4/AndroidManifest.xml @@ -83,11 +83,6 @@ > - - diff --git a/xwords4/android/XWords4/jni/Android.mk b/xwords4/android/XWords4/jni/Android.mk index 48b4e1e1a..e7eba2fa2 100644 --- a/xwords4/android/XWords4/jni/Android.mk +++ b/xwords4/android/XWords4/jni/Android.mk @@ -24,6 +24,7 @@ local_DEFINES += \ -DDROP_BITMAPS \ -DDISABLE_EMPTYTRAY_UNDO \ -DDISABLE_TILE_SEL \ + -DXWFEATURE_BOARDWORDS \ -DNODE_CAN_4 \ -DRELAY_ROOM_DEFAULT=\"\"\ -D__LITTLE_ENDIAN \ diff --git a/xwords4/android/XWords4/jni/utilwrapper.c b/xwords4/android/XWords4/jni/utilwrapper.c index ed0bc12df..a2a1a9cef 100644 --- a/xwords4/android/XWords4/jni/utilwrapper.c +++ b/xwords4/android/XWords4/jni/utilwrapper.c @@ -422,6 +422,21 @@ and_util_playerScoreHeld( XW_UtilCtxt* uc, XP_U16 player ) } #endif +#ifdef XWFEATURE_BOARDWORDS +static void +and_util_cellSquareHeld( XW_UtilCtxt* uc, XWStreamCtxt* words ) +{ + if ( NULL != words ) { + UTIL_CBK_HEADER( "cellSquareHeld", "(Ljava/lang/String;)V" ); + jstring jwords = + streamToJString( MPPARM(util->util.mpool) env, words ); + (*env)->CallVoidMethod( env, util->jutil, mid, jwords ); + (*env)->DeleteLocalRef( env, jwords ); + UTIL_CBK_TAIL(); + } +} +#endif + #ifndef XWFEATURE_STANDALONE_ONLY static void and_util_addrChange( XW_UtilCtxt* uc, const CommsAddrRec* oldAddr, @@ -512,6 +527,10 @@ makeUtil( MPFORMAL JNIEnv** envp, jobject jutil, CurGameInfo* gi, SET_PROC(playerScoreHeld); #endif +#ifdef XWFEATURE_BOARDWORDS + SET_PROC(cellSquareHeld); +#endif + #ifndef XWFEATURE_STANDALONE_ONLY SET_PROC(addrChange); #endif diff --git a/xwords4/android/XWords4/jni/xwjni.c b/xwords4/android/XWords4/jni/xwjni.c index 7ec9bae1a..18ac9bb90 100644 --- a/xwords4/android/XWords4/jni/xwjni.c +++ b/xwords4/android/XWords4/jni/xwjni.c @@ -1256,21 +1256,6 @@ Java_org_eehouse_android_xw4_jni_XwJNI_server_1endGame XWJNI_END(); } -JNIEXPORT jstring JNICALL -Java_org_eehouse_android_xw4_jni_XwJNI_model_1getWordsPlayed -( JNIEnv* env, jclass C, jint gamePtr, jint nMoves ) -{ - jstring result; - XWJNI_START_GLOBALS(); - XWStreamCtxt* stream = and_empty_stream( MPPARM(mpool) globals ); - model_getWordsPlayed( state->game.model, nMoves, stream ); - result = streamToJString( MPPARM(mpool) env, stream ); - (*env)->DeleteLocalRef( env, result ); - stream_destroy( stream ); - XWJNI_END(); - return result; -} - JNIEXPORT void JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_server_1sendChat ( JNIEnv* env, jclass C, jint gamePtr, jstring jmsg ) diff --git a/xwords4/android/XWords4/res/layout/game_config.xml b/xwords4/android/XWords4/res/layout/game_config.xml index b7d0c3dbe..f8e0744e7 100644 --- a/xwords4/android/XWords4/res/layout/game_config.xml +++ b/xwords4/android/XWords4/res/layout/game_config.xml @@ -47,6 +47,18 @@ android:layout_height="fill_parent" android:orientation="vertical"> + + + + + - - - - - + - - + diff --git a/xwords4/android/XWords4/res/menu/board_menu.xml b/xwords4/android/XWords4/res/menu/board_menu.xml index 0e4617561..8942cca64 100644 --- a/xwords4/android/XWords4/res/menu/board_menu.xml +++ b/xwords4/android/XWords4/res/menu/board_menu.xml @@ -30,8 +30,6 @@ - 2 || (m_notNetworkedGame && names.length > 1); LayoutInflater factory = LayoutInflater.from(this); + View.OnClickListener lstnr = new View.OnClickListener() { + @Override + public void onClick( View view ) { + m_whichPlayer = ((XWListItem)view).getPosition(); + showDialog( PLAYER_EDIT ); + } + }; + for ( int ii = 0; ii < names.length; ++ii ) { - final XWListItem view = (XWListItem)factory.inflate( R.layout.list_item, null ); view.setPosition( ii ); view.setText( names[ii] ); - view.setGravity( Gravity.CENTER ); + if ( m_gi.players[ii].isLocal ) { + view.setComment( m_gi.dictName(ii) ); + } if ( canDelete ) { view.setDeleteCallback( this ); } - view.setOnClickListener( new View.OnClickListener() { - @Override - public void onClick( View view ) { - m_whichPlayer = ((XWListItem)view).getPosition(); - showDialog( PLAYER_EDIT ); - } - } ); + view.setOnClickListener( lstnr ); m_playerLayout.addView( view ); view.setEnabled( !m_isLocked ); @@ -675,20 +678,6 @@ public class GameConfig extends XWActivity adjustPlayersLabel(); } // loadPlayersList - private String[] buildListWithBrowse( String[] input ) - { - Arrays.sort( input ); - int browsePosn = input.length; - String[] result = new String[browsePosn+1]; - result[browsePosn] = getString( R.string.download_dicts ); - - for ( int ii = 0; ii < browsePosn; ++ii ) { - String lang = input[ii]; - result[ii] = lang; - } - return result; - } - private void configDictSpinner( final Dialog dialog, LocalPlayer lp ) { Spinner dictsSpinner = @@ -738,6 +727,7 @@ public class GameConfig extends XWActivity m_gi.setLang( DictLangCache. getLangLangCode( GameConfig.this, chosen ) ); + loadPlayersList(); } } diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/LookupActivity.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/LookupView.java similarity index 69% rename from xwords4/android/XWords4/src/org/eehouse/android/xw4/LookupActivity.java rename to xwords4/android/XWords4/src/org/eehouse/android/xw4/LookupView.java index aa22916c3..98106f32b 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/LookupActivity.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/LookupView.java @@ -20,8 +20,9 @@ package org.eehouse.android.xw4; -import android.app.Activity; +import android.app.Dialog; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.content.res.Resources; import android.net.Uri; @@ -31,30 +32,25 @@ import android.view.Window; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.Button; +import android.widget.LinearLayout; import android.widget.ListView; import android.widget.TextView; import android.app.AlertDialog; import java.util.ArrayList; +import android.util.AttributeSet; import junit.framework.Assert; -public class LookupActivity extends XWListActivity +public class LookupView extends LinearLayout implements View.OnClickListener, - AdapterView.OnItemClickListener { - - public static final String WORDS = "WORDS"; - public static final String LANG = "LANG"; - public static final String STATE = "STATE"; - public static final String WORDINDEX = "WORDINDEX"; - public static final String URLINDEX = "URLINDEX"; + AdapterView.OnItemClickListener, + DialogInterface.OnDismissListener { private static final int STATE_DONE = 0; private static final int STATE_WORDS = 1; private static final int STATE_URLS = 2; private static final int STATE_LOOKUP = 3; - private static final int LOOKUP_ACTION = 1; - private static String[] s_langCodes; private static String[] s_lookupNames; private static String[] s_lookupUrls; @@ -71,26 +67,28 @@ public class LookupActivity extends XWListActivity private ArrayAdapter m_wordsAdapter; private Button m_doneButton; private TextView m_summary; + private Dialog m_dialog; + private Context m_context; + private int m_dlgId; + private ListView m_list; - @Override - protected void onCreate( Bundle savedInstanceState ) + public LookupView( Context cx, AttributeSet as ) { + super( cx, as ); + m_context = cx; + } + + public void setWords( String[] words, int lang ) { - super.onCreate( savedInstanceState ); + m_words = words; + setLang( lang ); - requestWindowFeature( Window.FEATURE_NO_TITLE ); + m_state = STATE_DONE; + adjustState( 1 ); - Intent intent = getIntent(); - m_words = intent.getStringArrayExtra( WORDS ); - m_lang = intent.getIntExtra( LANG, -1 ); - setLang( m_lang ); - - getBundledData( savedInstanceState ); - - setContentView( R.layout.lookup ); - - m_wordsAdapter = new ArrayAdapter( this, LIST_LAYOUT, + m_wordsAdapter = new ArrayAdapter( m_context, LIST_LAYOUT, m_words ); - getListView().setOnItemClickListener( this ); + m_list = (ListView)findViewById( R.id.list ); + m_list.setOnItemClickListener( this ); m_doneButton = (Button)findViewById( R.id.button_done ); m_doneButton.setOnClickListener( this ); @@ -99,6 +97,13 @@ public class LookupActivity extends XWListActivity switchState(); } + public void setDialog( Dialog dialog, int id ) + { + m_dialog = dialog; + m_dlgId = id; + m_dialog.setOnDismissListener( this ); + } + /* View.OnClickListener -- just the Done button */ public void onClick( View view ) { @@ -119,38 +124,10 @@ public class LookupActivity extends XWListActivity switchState( 1 ); } - @Override - protected void onSaveInstanceState( Bundle outState ) + /* DialogInterface.OnDismissListener interface */ + public void onDismiss( DialogInterface di ) { - super.onSaveInstanceState( outState ); - outState.putInt( STATE, m_state ); - outState.putInt( WORDINDEX, m_wordIndex ); - outState.putInt( URLINDEX, m_urlIndex ); - } - - ////////////////////////////////////////////////// - // DlgDelegate.DlgClickNotify interface - ////////////////////////////////////////////////// - @Override - public void dlgButtonClicked( int id, int which ) - { - if ( LOOKUP_ACTION == id - && AlertDialog.BUTTON_POSITIVE == which ) { - lookupWord( m_words[m_wordIndex], s_lookupUrls[m_urlIndex] ); - switchState( -1 ); - } - } - - private void getBundledData( Bundle bundle ) - { - if ( null == bundle ) { - m_state = STATE_DONE; - adjustState( 1 ); - } else { - m_state = bundle.getInt( STATE ); - m_wordIndex = bundle.getInt( WORDINDEX ); - m_urlIndex = bundle.getInt( URLINDEX ); - } + m_dialog.getOwnerActivity().removeDialog( m_dlgId ); } private void adjustState( int incr ) @@ -180,28 +157,23 @@ public class LookupActivity extends XWListActivity { switch( m_state ) { case STATE_DONE: - finish(); + m_dialog.dismiss(); break; case STATE_WORDS: - getListView().setAdapter( m_wordsAdapter ); + m_list.setAdapter( m_wordsAdapter ); setSummary( R.string.title_lookup ); m_doneButton.setText( R.string.button_done ); break; case STATE_URLS: - getListView().setAdapter( s_urlsAdapter ); + m_list.setAdapter( s_urlsAdapter ); setSummary( m_words[m_wordIndex] ); - String txt = Utils.format( this, R.string.button_donef, + String txt = Utils.format( m_context, R.string.button_donef, m_words[m_wordIndex] ); m_doneButton.setText( txt ); break; case STATE_LOOKUP: - if ( 1 >= s_lookupUrls.length ) { - showNotAgainDlgThen( R.string.not_again_needUrlsForLang, - R.string.key_na_needUrlsForLang, - LOOKUP_ACTION ); - } else { - dlgButtonClicked( LOOKUP_ACTION, AlertDialog.BUTTON_POSITIVE ); - } + lookupWord( m_words[m_wordIndex], s_lookupUrls[m_urlIndex] ); + switchState( -1 ); break; default: Assert.fail(); @@ -221,7 +193,7 @@ public class LookupActivity extends XWListActivity intent.setFlags( Intent.FLAG_ACTIVITY_NEW_TASK ); try { - startActivity( intent ); + m_context.startActivity( intent ); } catch ( android.content.ActivityNotFoundException anfe ) { Utils.logf( "%s", anfe.toString() ); } @@ -248,7 +220,7 @@ public class LookupActivity extends XWListActivity } s_lookupNames = tmpNames.toArray( new String[tmpNames.size()] ); s_lookupUrls = tmpUrls.toArray( new String[tmpUrls.size()] ); - s_urlsAdapter = new ArrayAdapter( this, LIST_LAYOUT, + s_urlsAdapter = new ArrayAdapter( m_context, LIST_LAYOUT, s_lookupNames ); s_lang = lang; } // initLookup @@ -256,13 +228,12 @@ public class LookupActivity extends XWListActivity private void setSummary( int id ) { - m_summary.setText( getString( id ) ); + m_summary.setText( m_context.getString( id ) ); } private void setSummary( String word ) { - String title = Utils.format( this, R.string.pick_url_titlef, word ); + String title = Utils.format( m_context, R.string.pick_url_titlef, word ); m_summary.setText( title ); } - } diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/CurGameInfo.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/CurGameInfo.java index b49dc41c9..18e930bdc 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/CurGameInfo.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/CurGameInfo.java @@ -303,6 +303,12 @@ public class CurGameInfo { return dname; } + public String dictName( int indx ) + { + LocalPlayer lp = players[indx]; + return dictName( lp ); + } + public boolean addPlayer() { boolean added = nPlayers < MAX_NUM_PLAYERS; diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/JNIThread.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/JNIThread.java index 284b1f9df..f272e64a3 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/JNIThread.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/JNIThread.java @@ -77,7 +77,6 @@ public class JNIThread extends Thread { CMD_RESEND, CMD_HISTORY, CMD_FINAL, - CMD_WORDS, CMD_ENDGAME, CMD_POST_OVER, CMD_SENDCHAT, @@ -493,13 +492,6 @@ public class JNIThread extends Thread { } break; - case CMD_WORDS: - int nMoves = ((Integer)args[0]).intValue(); - String words = XwJNI.model_getWordsPlayed( m_jniGamePtr, - nMoves ); - Message.obtain( m_handler, GOT_WORDS, words ).sendToTarget(); - break; - case CMD_ENDGAME: XwJNI.server_endGame( m_jniGamePtr ); draw = true; diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/UtilCtxt.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/UtilCtxt.java index 72efae8c5..646757ce4 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/UtilCtxt.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/UtilCtxt.java @@ -49,6 +49,7 @@ public interface UtilCtxt { void bonusSquareHeld( int bonus ); void playerScoreHeld( int player ); + void cellSquareHeld( String words ); static final int STRD_ROBOT_TRADED = 1; static final int STR_ROBOT_MOVED = 2; diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/UtilCtxtImpl.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/UtilCtxtImpl.java index cab9c7840..255f7da0b 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/UtilCtxtImpl.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/UtilCtxtImpl.java @@ -91,6 +91,10 @@ public class UtilCtxtImpl implements UtilCtxt { { } + public void cellSquareHeld( String words ) + { + } + public String getUserString( int stringCode ) { int id = 0; diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/XwJNI.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/XwJNI.java index c2d4e7fb6..62ecd9209 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/XwJNI.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/XwJNI.java @@ -211,9 +211,6 @@ public class XwJNI { public static native int model_getNMoves( int gamePtr ); public static native String model_getPlayersLastScore( int gamePtr, int player ); - public static native String model_getWordsPlayed( int gamePtr, - int nTurns ); - // Server public static native void server_reset( int gamePtr ); public static native void server_handleUndo( int gamePtr ); diff --git a/xwords4/common/board.c b/xwords4/common/board.c index 476cc61f9..b279f3fae 100644 --- a/xwords4/common/board.c +++ b/xwords4/common/board.c @@ -726,7 +726,12 @@ hideMiniWindow( BoardCtxt* board, XP_Bool destroy, MiniWindowType winType ) #endif static XP_Bool -warnBadWords( const XP_UCHAR* word, XP_Bool isLegal, void* closure ) +warnBadWords( const XP_UCHAR* word, XP_Bool isLegal, +#ifdef XWFEATURE_BOARDWORDS + const MoveInfo* XP_UNUSED(movei), + XP_U16 XP_UNUSED(start), XP_U16 XP_UNUSED(end), +#endif + void* closure ) { XP_Bool ok = XP_TRUE; if ( !isLegal ) { @@ -965,15 +970,37 @@ timerFiredForPen( BoardCtxt* board ) draw = dragDropSetAdd( board ); #endif } else { - XWBonusType bonus; - bonus = util_getSquareBonus( board->util, board->model, - col, row ); - if ( bonus != BONUS_NONE ) { -#ifdef XWFEATURE_MINIWIN - text = draw_getMiniWText( board->draw, (XWMiniTextType)bonus ); -#else - util_bonusSquareHeld( board->util, bonus ); + XP_Bool listWords = XP_FALSE; +#ifdef XWFEATURE_BOARDWORDS + XP_U16 modelCol, modelRow; + flipIf( board, col, row, &modelCol, &modelRow ); + listWords = model_getTile( board->model, modelCol, modelRow, + XP_TRUE, board->selPlayer, NULL, + NULL, NULL, NULL ); + if ( listWords ) { + XWStreamCtxt* stream = + mem_stream_make( MPPARM(board->mpool) + util_getVTManager(board->util), NULL, + CHANNEL_NONE, + (MemStreamCloseCallback)NULL ); + model_listWordsThrough( board->model, modelCol, modelRow, + stream ); + util_cellSquareHeld( board->util, stream ); + stream_destroy( stream ); + } #endif + if ( !listWords ) { + XWBonusType bonus; + bonus = util_getSquareBonus( board->util, board->model, + col, row ); + if ( bonus != BONUS_NONE ) { +#ifdef XWFEATURE_MINIWIN + text = draw_getMiniWText( board->draw, + (XWMiniTextType)bonus ); +#else + util_bonusSquareHeld( board->util, bonus ); +#endif + } } } board->penTimerFired = XP_TRUE; diff --git a/xwords4/common/model.c b/xwords4/common/model.c index ee94fbf3e..a9871c5fb 100644 --- a/xwords4/common/model.c +++ b/xwords4/common/model.c @@ -75,7 +75,11 @@ 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 ); +static XP_Bool recordWord( const XP_UCHAR* word, XP_Bool isLegal, +#ifdef XWFEATURE_BOARDWORDS + const MoveInfo* movei, XP_U16 start, XP_U16 end, +#endif + void* clsur ); /***************************************************************************** * @@ -1948,7 +1952,11 @@ typedef struct _FirstWordData { } FirstWordData; static XP_Bool -getFirstWord( const XP_UCHAR* word, XP_Bool isLegal, void* closure ) +getFirstWord( const XP_UCHAR* word, XP_Bool isLegal, +#ifdef XWFEATURE_BOARDWORDS + const MoveInfo* XP_UNUSED(movei), XP_U16 XP_UNUSED(start), XP_U16 XP_UNUSED(end), +#endif + void* closure ) { if ( isLegal ) { FirstWordData* data = (FirstWordData*)closure; @@ -2034,16 +2042,26 @@ model_recentPassCountOk( ModelCtxt* model ) return count < okCount; } -static XP_Bool -recordWord( const XP_UCHAR* word, XP_Bool XP_UNUSED(isLegal), void* closure ) +static void +appendWithCR( XWStreamCtxt* stream, const XP_UCHAR* word, XP_U16* counter ) { - RecordWordsInfo* info = (RecordWordsInfo*)closure; - XWStreamCtxt* stream = info->stream; - XP_LOGF( "%s(%s)", __func__, word ); - if ( 0 < info->nWords++ ) { + if ( 0 < (*counter)++ ) { stream_putU8( stream, '\n' ); } stream_catString( stream, word ); +} + +static XP_Bool +recordWord( const XP_UCHAR* word, XP_Bool XP_UNUSED(isLegal), +#ifdef XWFEATURE_BOARDWORDS + const MoveInfo* XP_UNUSED(movei), XP_U16 XP_UNUSED(start), + XP_U16 XP_UNUSED(end), +#endif + void* closure ) + +{ + RecordWordsInfo* info = (RecordWordsInfo*)closure; + appendWithCR( info->stream, word, &info->nWords ); return XP_TRUE; } @@ -2057,32 +2075,63 @@ model_initWordCounter( ModelCtxt* model, XWStreamCtxt* stream ) return &model->vol.wni; } +#ifdef XWFEATURE_BOARDWORDS +typedef struct _ListWordsThroughInfo { + XWStreamCtxt* stream; + XP_U16 col, row; + XP_U16 nWords; +} ListWordsThroughInfo; + +static XP_Bool +listWordsThrough( const XP_UCHAR* word, XP_Bool XP_UNUSED(isLegal), + const MoveInfo* movei, XP_U16 start, XP_U16 end, + void* closure ) +{ + ListWordsThroughInfo* info = (ListWordsThroughInfo*)closure; + + XP_Bool contained = XP_FALSE; + if ( movei->isHorizontal && movei->commonCoord == info->row ) { + contained = start <= info->col && end >= info->col; + } else if ( !movei->isHorizontal && movei->commonCoord == info->col ) { + contained = start <= info->row && end >= info->row; + } + + if ( contained ) { + appendWithCR( info->stream, word, &info->nWords ); + } + + return XP_TRUE; +} + +/* List every word played that includes the tile on {col,row}. + * + * How? Undo backwards until we find the move that placed that tile.*/ void -model_getWordsPlayed( ModelCtxt* model, XP_U16 nTurns, XWStreamCtxt* stream ) +model_listWordsThrough( ModelCtxt* model, XP_U16 col, XP_U16 row, + XWStreamCtxt* stream ) { XP_ASSERT( !!stream ); StackCtxt* stack = model->vol.stack; StackCtxt* tmpStack = stack_copy( stack ); XP_U16 nPlayers = model->nPlayers; - XP_U16 nEntries = stack_getNEntries( stack ); - nEntries -= nPlayers; /* skip assignments */ - if ( nTurns > nEntries ) { - nTurns = nEntries; - } + XP_U16 nEntries = stack_getNEntries( stack ) - nPlayers; /* skip assignments */ - if ( model_undoLatestMoves( model, NULL, nTurns, NULL, NULL ) ) { - WordNotifierInfo* ni = model_initWordCounter( model, stream ); + if ( model_undoLatestMoves( model, NULL, nEntries, NULL, NULL ) ) { + ListWordsThroughInfo lwtInfo = { .stream = stream, .col = col, + .row = row, .nWords = 0, + }; + WordNotifierInfo ni = { .proc = listWordsThrough, .closure = &lwtInfo }; /* 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, ni, (MovePrintFuncPre)NULL, + buildModelFromStack( model, tmpStack, XP_TRUE, nPlayers, + (XWStreamCtxt*)NULL, &ni, (MovePrintFuncPre)NULL, (MovePrintFuncPost)NULL, NULL ); } stack_destroy( tmpStack ); } +#endif XP_Bool model_getPlayersLastScore( ModelCtxt* model, XP_S16 player, diff --git a/xwords4/common/model.h b/xwords4/common/model.h index b24b2371f..2ac3b2127 100644 --- a/xwords4/common/model.h +++ b/xwords4/common/model.h @@ -239,6 +239,10 @@ void model_countAllTrayTiles( ModelCtxt* model, XP_U16* counts, /********************* scoring ********************/ typedef XP_Bool (*WordNotifierProc)( const XP_UCHAR* word, XP_Bool isLegal, +#ifdef XWFEATURE_BOARDWORDS + const MoveInfo* movei, XP_U16 start, + XP_U16 end, +#endif void* closure ); typedef struct WordNotifierInfo { WordNotifierProc proc; @@ -252,8 +256,10 @@ XP_S16 model_getPlayerScore( ModelCtxt* model, XP_S16 player ); XP_Bool model_getPlayersLastScore( ModelCtxt* model, XP_S16 player, XP_UCHAR* expl, XP_U16* explLen ); -void model_getWordsPlayed( ModelCtxt* model, XP_U16 nTurns, - XWStreamCtxt* stream ); +#ifdef XWFEATURE_BOARDWORDS +void model_listWordsThrough( ModelCtxt* model, XP_U16 col, XP_U16 row, + XWStreamCtxt* stream ); +#endif /* Have there been too many passes (so game should end)? */ XP_Bool model_recentPassCountOk( ModelCtxt* model ); diff --git a/xwords4/common/mscore.c b/xwords4/common/mscore.c index 6be9b12ae..012cada53 100644 --- a/xwords4/common/mscore.c +++ b/xwords4/common/mscore.c @@ -40,9 +40,9 @@ static XP_U16 find_start( const ModelCtxt* model, XP_U16 col, XP_U16 row, static XP_S16 checkScoreMove( ModelCtxt* model, XP_S16 turn, EngineCtxt* engine, XWStreamCtxt* stream, XP_Bool silent, WordNotifierInfo* notifyInfo ); -static XP_U16 scoreWord( const ModelCtxt* model, XP_U16 turn, MoveInfo* movei, - EngineCtxt* engine, XWStreamCtxt* stream, - WordNotifierInfo* notifyInfo ); +static XP_U16 scoreWord( const ModelCtxt* model, XP_U16 turn, + const MoveInfo* movei, EngineCtxt* engine, + XWStreamCtxt* stream, WordNotifierInfo* notifyInfo ); /* for formatting when caller wants an explanation of the score. These live in separate function called only when stream != NULL so that they'll have @@ -553,7 +553,7 @@ tile_multiplier( const ModelCtxt* model, XP_U16 col, XP_U16 row ) static XP_U16 scoreWord( const ModelCtxt* model, XP_U16 turn, - MoveInfo* movei, /* new tiles */ + const MoveInfo* movei, /* new tiles */ EngineCtxt* engine,/* for crosswise caching */ XWStreamCtxt* stream, WordNotifierInfo* notifyInfo ) @@ -567,7 +567,7 @@ scoreWord( const ModelCtxt* model, XP_U16 turn, XP_U16 start, end; XP_U16* incr; XP_U16 col, row; - MoveInfoTile* tiles = movei->tiles; + const MoveInfoTile* tiles = movei->tiles; XP_U16 firstCoord = tiles->varCoord; DictionaryCtxt* dict = model_getPlayerDict( model, turn ); @@ -671,7 +671,11 @@ scoreWord( const ModelCtxt* model, XP_U16 turn, XP_UCHAR buf[(MAX_ROWS*2)+1]; dict_tilesToString( dict, checkWordBuf, len, buf, sizeof(buf) ); - (void)(*notifyInfo->proc)( buf, legal, notifyInfo->closure ); + (void)(*notifyInfo->proc)( buf, legal, +#ifdef XWFEATURE_BOARDWORDS + movei, start, end, +#endif + notifyInfo->closure ); } if ( !!stream ) { diff --git a/xwords4/common/pool.c b/xwords4/common/pool.c index 643a4baef..b2491d418 100644 --- a/xwords4/common/pool.c +++ b/xwords4/common/pool.c @@ -212,6 +212,7 @@ pool_removeTiles( PoolContext* pool, TrayTileSet* tiles ) --pool->lettersLeft[tile]; --pool->numTilesLeft; } + XP_LOGF( "%s: %d tiles left in pool", __func__, pool->numTilesLeft ); } /* pool_removeTiles */ XP_U16 diff --git a/xwords4/common/server.c b/xwords4/common/server.c index 5b471e715..1a2eada71 100644 --- a/xwords4/common/server.c +++ b/xwords4/common/server.c @@ -1740,7 +1740,12 @@ server_setGameOverListener( ServerCtxt* server, GameOverListener gol, } /* server_setGameOverListener */ static XP_Bool -storeBadWords( const XP_UCHAR* word, XP_Bool isLegal, void* closure ) +storeBadWords( const XP_UCHAR* word, XP_Bool isLegal, +#ifdef XWFEATURE_BOARDWORDS + const MoveInfo* XP_UNUSED(movei), XP_U16 XP_UNUSED(start), + XP_U16 XP_UNUSED(end), +#endif + void* closure ) { if ( !isLegal ) { ServerCtxt* server = (ServerCtxt*)closure; diff --git a/xwords4/common/util.h b/xwords4/common/util.h index 4ba082f01..b1da52012 100644 --- a/xwords4/common/util.h +++ b/xwords4/common/util.h @@ -162,6 +162,9 @@ typedef struct UtilVtable { void (*m_util_bonusSquareHeld)( XW_UtilCtxt* uc, XWBonusType bonus ); void (*m_util_playerScoreHeld)( XW_UtilCtxt* uc, XP_U16 player ); #endif +#ifdef XWFEATURE_BOARDWORDS + void (*m_util_cellSquareHeld)( XW_UtilCtxt* uc, XWStreamCtxt* words ); +#endif #ifndef XWFEATURE_STANDALONE_ONLY void (*m_util_addrChange)( XW_UtilCtxt* uc, const CommsAddrRec* oldAddr, @@ -271,7 +274,10 @@ struct XW_UtilCtxt { # define util_playerScoreHeld( uc, player ) \ (uc)->vtable->m_util_playerScoreHeld( (uc), (player) ) #endif - +#ifdef XWFEATURE_BOARDWORDS +#define util_cellSquareHeld(uc, s) \ + (uc)->vtable->m_util_cellSquareHeld( (uc), (s) ) +#endif #ifndef XWFEATURE_STANDALONE_ONLY # define util_addrChange( uc, addro, addrn ) \ (uc)->vtable->m_util_addrChange((uc), (addro), (addrn)) diff --git a/xwords4/linux/Makefile b/xwords4/linux/Makefile index 9be2e4f46..b3dbcbcbd 100644 --- a/xwords4/linux/Makefile +++ b/xwords4/linux/Makefile @@ -38,6 +38,7 @@ ifdef CURSES_SMALL_SCREEN DO_CURSES += -DCURSES_SMALL_SCREEN endif DO_GTK = -DPLATFORM_GTK +DO_GTK += -DXWFEATURE_BOARDWORDS # DO_GTK += -DUSE_CAIRO # uncomment for standalone build diff --git a/xwords4/linux/gtkmain.c b/xwords4/linux/gtkmain.c index 862c97e6e..4f1f3985c 100644 --- a/xwords4/linux/gtkmain.c +++ b/xwords4/linux/gtkmain.c @@ -1477,22 +1477,15 @@ cancelTimer( GtkAppGlobals* globals, XWTimerReason why ) } /* cancelTimer */ static gint -pentimer_idle_func( gpointer data ) +pen_timer_func( gpointer data ) { GtkAppGlobals* globals = (GtkAppGlobals*)data; - struct timeval tv; - XP_Bool callAgain = XP_TRUE; - gettimeofday( &tv, NULL ); + if ( linuxFireTimer( &globals->cGlobals, TIMER_PENDOWN ) ) { + board_draw( globals->cGlobals.game.board ); + } - if ( (tv.tv_usec - globals->penTv.tv_usec) >= globals->penTimerInterval) { - if ( linuxFireTimer( &globals->cGlobals, TIMER_PENDOWN ) ) { - board_draw( globals->cGlobals.game.board ); - } - callAgain = XP_FALSE; - } - - return callAgain; + return XP_FALSE; } /* pentimer_idle_func */ static gint @@ -1546,11 +1539,10 @@ gtk_util_setTimer( XW_UtilCtxt* uc, XWTimerReason why, cancelTimer( globals, why ); if ( why == TIMER_PENDOWN ) { - /* half a second */ - globals->penTimerInterval = 50 * 10000; - - (void)gettimeofday( &globals->penTv, NULL ); - newSrc = g_idle_add( pentimer_idle_func, globals ); + if ( 0 != globals->timerSources[why-1] ) { + g_source_remove( globals->timerSources[why-1] ); + } + newSrc = g_timeout_add( 1000, pen_timer_func, globals ); } else if ( why == TIMER_TIMERTICK ) { /* one second */ globals->scoreTimerInterval = 100 * 10000; @@ -1718,15 +1710,6 @@ gtk_util_playerScoreHeld( XW_UtilCtxt* uc, XP_U16 player ) GtkAppGlobals* globals = (GtkAppGlobals*)uc->closure; -#if 1 - XP_USE( player ); - XWStreamCtxt* stream = - mem_stream_make( MEMPOOL globals->cGlobals.params->vtMgr, globals, - CHANNEL_NONE, catOnClose ); - (void)model_getWordsPlayed( globals->cGlobals.game.model, 1000, stream ); - stream_destroy( stream ); -#else - XP_UCHAR scoreExpl[48]; XP_U16 explLen = sizeof(scoreExpl); @@ -1734,7 +1717,15 @@ gtk_util_playerScoreHeld( XW_UtilCtxt* uc, XP_U16 player ) player, scoreExpl, &explLen ) ) { XP_LOGF( "got: %s", scoreExpl ); } +} #endif + +#ifdef XWFEATURE_BOARDWORDS +static void +gtk_util_cellSquareHeld( XW_UtilCtxt* uc, XWStreamCtxt* words ) +{ + XP_USE( uc ); + XP_USE( words ); } #endif @@ -1960,6 +1951,9 @@ setupGtkUtilCallbacks( GtkAppGlobals* globals, XW_UtilCtxt* util ) util->vtable->m_util_bonusSquareHeld = gtk_util_bonusSquareHeld; util->vtable->m_util_playerScoreHeld = gtk_util_playerScoreHeld; #endif +#ifdef XWFEATURE_BOARDWORDS + util->vtable->m_util_cellSquareHeld = gtk_util_cellSquareHeld; +#endif util->closure = globals; } /* setupGtkUtilCallbacks */ diff --git a/xwords4/linux/gtkmain.h b/xwords4/linux/gtkmain.h index 92de46fae..f99713213 100644 --- a/xwords4/linux/gtkmain.h +++ b/xwords4/linux/gtkmain.h @@ -106,8 +106,6 @@ typedef struct GtkAppGlobals { guint idleID; - struct timeval penTv; /* for timer */ - XP_U32 penTimerInterval; struct timeval scoreTv; /* for timer */ XP_U32 scoreTimerInterval;