From c271202faa52e5c647aa5b8772730b2b05e0c64f Mon Sep 17 00:00:00 2001 From: Eric House Date: Mon, 20 Feb 2017 07:20:19 -0800 Subject: [PATCH] make bad phonies alert non-blocking Continue conversion of alerts that required blocking the JNI thread. Now board_commitTurn() takes a second boolean indicating whether phonies found have been approved by user. Common code informs user, and if he approves client code calls board_commitTurn() again. In case where turn's lost there's no call to make back, but there's the undesirable change that if a robot moves next its move will be reported on top of the turn-lost alert. Ideally new alerts would appear under, not on top of, those that have not yet been dismissed. --- .../eehouse/android/xw4/BoardDelegate.java | 45 ++++++------- .../java/org/eehouse/android/xw4/DlgID.java | 3 +- .../eehouse/android/xw4/jni/JNIThread.java | 16 ++--- .../org/eehouse/android/xw4/jni/UtilCtxt.java | 2 +- .../eehouse/android/xw4/jni/UtilCtxtImpl.java | 5 +- .../org/eehouse/android/xw4/jni/XwJNI.java | 4 +- .../app/src/main/res/values/strings.xml | 2 + xwords4/android/jni/utilwrapper.c | 18 +++-- xwords4/android/jni/xwjni.c | 6 +- xwords4/common/board.c | 63 +++++++++--------- xwords4/common/board.h | 3 +- xwords4/common/boardp.h | 1 - xwords4/common/server.c | 2 +- xwords4/common/tray.c | 2 +- xwords4/common/util.h | 6 +- xwords4/linux/cursesmain.c | 19 +++--- xwords4/linux/gtkboard.c | 65 +++++++++---------- 17 files changed, 133 insertions(+), 129 deletions(-) diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/BoardDelegate.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/BoardDelegate.java index 21514995e..ed812750b 100644 --- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/BoardDelegate.java +++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/BoardDelegate.java @@ -275,7 +275,7 @@ public class BoardDelegate extends DelegateBase lstnr = new OnClickListener() { public void onClick( DialogInterface dialog, int whichButton ) { - handleViaThread( JNICmd.CMD_COMMIT_TRUE ); + handleViaThread( JNICmd.CMD_COMMIT, true, true ); } }; dialog = ab.setMessage( msg ) @@ -286,8 +286,21 @@ public class BoardDelegate extends DelegateBase } break; - case DLG_BADWORDS_BLK: - checkBlocking(); + case NOTIFY_BADWORDS: { + lstnr = new OnClickListener() { + public void onClick( DialogInterface dlg, int bx ) { + handleViaThread( JNICmd.CMD_COMMIT, true, false ); + } + }; + dialog = ab.setTitle( R.string.phonies_found_title ) + .setMessage( (String)params[0] ) + .setPositiveButton( R.string.button_yes, lstnr ) + .setNegativeButton( android.R.string.cancel, null ) + .create(); + } + break; + + case DLG_BADWORDS: case DLG_SCORES: { int title = (Integer)params[0]; String msg = (String)params[1]; @@ -917,7 +930,7 @@ public class BoardDelegate extends DelegateBase break; case R.id.board_menu_trade_commit: - cmd = JNICmd.CMD_COMMIT_FALSE; + cmd = JNICmd.CMD_COMMIT; break; case R.id.board_menu_trade_cancel: cmd = JNICmd.CMD_CANCELTRADE; @@ -1045,7 +1058,7 @@ public class BoardDelegate extends DelegateBase Utils.launchSettings( m_activity ); break; case COMMIT_ACTION: - cmd = JNICmd.CMD_COMMIT_FALSE; + cmd = JNICmd.CMD_COMMIT; break; case SHOW_EXPL_ACTION: showToast( m_toastStr ); @@ -1240,7 +1253,7 @@ public class BoardDelegate extends DelegateBase public void onClick( View view ) { if ( view == m_exchCommmitButton ) { - handleViaThread( JNICmd.CMD_COMMIT_FALSE ); + handleViaThread( JNICmd.CMD_COMMIT ); } else if ( view == m_exchCancelButton ) { handleViaThread( JNICmd.CMD_CANCELTRADE ); } @@ -2034,33 +2047,21 @@ public class BoardDelegate extends DelegateBase handleViaThread( JNICmd.CMD_POST_OVER ); } - // public void yOffsetChange( int maxOffset, int oldOffset, int newOffset ) - // { - // DbgUtils.logf( "yOffsetChange(maxOffset=%d)", maxOffset ); - // m_view.setVerticalScrollBarEnabled( maxOffset > 0 ); - // } @Override - public boolean warnIllegalWord( String dict, String[] words, int turn, + public void notifyIllegalWords( String dict, String[] words, int turn, boolean turnLost ) { - boolean accept = turnLost; - String wordsString = TextUtils.join( ", ", words ); String message = getString( R.string.ids_badwords_fmt, wordsString, dict ); if ( turnLost ) { - waitBlockingDialog( DlgID.DLG_BADWORDS_BLK, 0, R.string.badwords_title, + showDialogFragment( DlgID.DLG_BADWORDS, R.string.badwords_title, message + getString( R.string.badwords_lost ) ); } else { - Assert.fail(); - // String dlgBytes = message + getString( R.string.badwords_accept ); - // accept = 0 != waitBlockingDialog( DlgID.QUERY_REQUEST_BLK, 0, - // R.string.query_title, - // dlgBytes ); + String msg = message + getString( R.string.badwords_accept ); + showDialogFragment( DlgID.NOTIFY_BADWORDS, msg ); } - - return accept; } // Let's have this block in case there are multiple messages. If diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DlgID.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DlgID.java index 9dda4edbc..21ab933b0 100644 --- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DlgID.java +++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DlgID.java @@ -57,7 +57,8 @@ public enum DlgID { , WARN_NODICT , WARN_NODICT_NEW , WARN_NODICT_SUBST - , DLG_BADWORDS_BLK + , DLG_BADWORDS + , NOTIFY_BADWORDS , QUERY_MOVE , QUERY_TRADE , PICK_TILE_REQUESTBLANK diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/JNIThread.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/JNIThread.java index a25fedee4..14d644498 100644 --- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/JNIThread.java +++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/JNIThread.java @@ -71,8 +71,7 @@ public class JNIThread extends Thread { CMD_KEYDOWN, CMD_KEYUP, CMD_TIMER_FIRED, - CMD_COMMIT_FALSE, - CMD_COMMIT_TRUE, + CMD_COMMIT, CMD_JUGGLE, CMD_FLIP, CMD_TOGGLE_TRAY, @@ -544,13 +543,14 @@ public class JNIThread extends Thread { draw = processKeyEvent( elem.m_cmd, (XwJNI.XP_Key)args[0], barr ); break; - case CMD_COMMIT_FALSE: - draw = XwJNI.board_commitTurn( m_jniGamePtr, false ); + case CMD_COMMIT: + boolean phoniesConfirmed = args.length < 1 + ? false : (Boolean)args[0]; + boolean turnConfirmed = args.length < 2 + ? false : (Boolean)args[1]; + draw = XwJNI.board_commitTurn( m_jniGamePtr, phoniesConfirmed, + turnConfirmed ); break; - case CMD_COMMIT_TRUE: - draw = XwJNI.board_commitTurn( m_jniGamePtr, true ); - break; - case CMD_JUGGLE: draw = XwJNI.board_juggleTray( m_jniGamePtr ); break; diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/UtilCtxt.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/UtilCtxt.java index 7f53c5874..28732ffb0 100644 --- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/UtilCtxt.java +++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/UtilCtxt.java @@ -141,7 +141,7 @@ public interface UtilCtxt { // Don't need this unless we have a scroll thumb to indicate position //void yOffsetChange( int maxOffset, int oldOffset, int newOffset ); - boolean warnIllegalWord( String dict, String[] words, int turn, + void notifyIllegalWords( String dict, String[] words, int turn, boolean turnLost ); void showChat( String msg, int fromIndx, String fromName ); diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/UtilCtxtImpl.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/UtilCtxtImpl.java index 0139bcea0..cdae748a7 100644 --- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/UtilCtxtImpl.java +++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/UtilCtxtImpl.java @@ -282,11 +282,10 @@ public class UtilCtxtImpl implements UtilCtxt { subclassOverride( "notifyGameOver" ); } - public boolean warnIllegalWord( String dict, String[] words, int turn, + public void notifyIllegalWords( String dict, String[] words, int turn, boolean turnLost ) { - subclassOverride( "warnIllegalWord" ); - return false; + subclassOverride( "notifyIllegalWords" ); } // These need to go into some sort of chat DB, not dropped. diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/XwJNI.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/XwJNI.java index 8f1bd33f6..55f0401c4 100644 --- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/XwJNI.java +++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/XwJNI.java @@ -299,7 +299,9 @@ public class XwJNI { public static native boolean board_showTray( GamePtr gamePtr ); public static native boolean board_toggle_showValues( GamePtr gamePtr ); public static native boolean board_commitTurn( GamePtr gamePtr, - boolean alreadyConfirmed ); + boolean phoniesConfirmed, + boolean turnConfirmed ); + public static native boolean board_flip( GamePtr gamePtr ); public static native boolean board_replaceTiles( GamePtr gamePtr ); public static native int board_getSelPlayer( GamePtr gamePtr ); diff --git a/xwords4/android/app/src/main/res/values/strings.xml b/xwords4/android/app/src/main/res/values/strings.xml index 314c612f1..aef9c8a32 100644 --- a/xwords4/android/app/src/main/res/values/strings.xml +++ b/xwords4/android/app/src/main/res/values/strings.xml @@ -897,6 +897,8 @@ found in the word list) --> Handle phonies + Phonies found + Timer minutes per player diff --git a/xwords4/android/jni/utilwrapper.c b/xwords4/android/jni/utilwrapper.c index e9d465ec5..f7125aa9c 100644 --- a/xwords4/android/jni/utilwrapper.c +++ b/xwords4/android/jni/utilwrapper.c @@ -454,25 +454,23 @@ and_util_getUserQuantityString( XW_UtilCtxt* uc, XP_U16 stringCode, XP_U16 quant return result; } -static XP_Bool -and_util_warnIllegalWord( XW_UtilCtxt* uc, BadWordInfo* bwi, - XP_U16 turn, XP_Bool turnLost ) +static void +and_util_notifyIllegalWords( XW_UtilCtxt* uc, BadWordInfo* bwi, + XP_U16 turn, XP_Bool turnLost ) { - jboolean result = XP_FALSE; - UTIL_CBK_HEADER("warnIllegalWord", - "(Ljava/lang/String;[Ljava/lang/String;IZ)Z" ); + UTIL_CBK_HEADER("notifyIllegalWords", + "(Ljava/lang/String;[Ljava/lang/String;IZ)V" ); XP_ASSERT( bwi->nWords > 0 ); if ( bwi->nWords > 0 ) { jobjectArray jwords = makeStringArray( env, bwi->nWords, (const XP_UCHAR**)bwi->words ); XP_ASSERT( !!bwi->dictName ); jstring jname = (*env)->NewStringUTF( env, bwi->dictName ); - result = (*env)->CallBooleanMethod( env, util->jutil, mid, - jname, jwords, turn, turnLost ); + (*env)->CallVoidMethod( env, util->jutil, mid, + jname, jwords, turn, turnLost ); deleteLocalRefs( env, jwords, jname, DELETE_NO_REF ); } UTIL_CBK_TAIL(); - return result; } #ifdef XWFEATURE_CHAT @@ -732,7 +730,7 @@ makeUtil( MPFORMAL EnvThreadInfo* ti, jobject jutil, CurGameInfo* gi, SET_PROC(makeEmptyDict); SET_PROC(getUserString); SET_PROC(getUserQuantityString); - SET_PROC(warnIllegalWord); + SET_PROC(notifyIllegalWords); #ifdef XWFEATURE_CHAT SET_PROC(showChat); #endif diff --git a/xwords4/android/jni/xwjni.c b/xwords4/android/jni/xwjni.c index a558366ba..985a91325 100644 --- a/xwords4/android/jni/xwjni.c +++ b/xwords4/android/jni/xwjni.c @@ -1282,11 +1282,13 @@ Java_org_eehouse_android_xw4_jni_XwJNI_board_1toggle_1showValues JNIEXPORT jboolean JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_board_1commitTurn -(JNIEnv* env, jclass C, GamePtrType gamePtr, jboolean alreadyConfirmed) +(JNIEnv* env, jclass C, GamePtrType gamePtr, jboolean phoniesConfirmed, + jboolean turnConfirmed) { jboolean result; XWJNI_START(); - result = board_commitTurn( state->game.board, alreadyConfirmed ); + result = board_commitTurn( state->game.board, phoniesConfirmed, + turnConfirmed ); XWJNI_END(); return result; } diff --git a/xwords4/common/board.c b/xwords4/common/board.c index ba6124116..e8a66ba5a 100644 --- a/xwords4/common/board.c +++ b/xwords4/common/board.c @@ -984,8 +984,14 @@ hideMiniWindow( BoardCtxt* board, XP_Bool destroy, MiniWindowType winType ) #endif #endif +typedef struct _BadWordList { + BadWordInfo bwi; + XP_UCHAR buf[256]; + XP_U16 index; +} BadWordList; + static void -warnBadWords( const XP_UCHAR* word, XP_Bool isLegal, +saveBadWords( const XP_UCHAR* word, XP_Bool isLegal, const DictionaryCtxt* XP_UNUSED(dict), #ifdef XWFEATURE_BOARDWORDS const MoveInfo* XP_UNUSED(movei), @@ -993,22 +999,14 @@ warnBadWords( const XP_UCHAR* word, XP_Bool isLegal, #endif void* closure ) { - XP_Bool ok = XP_TRUE; if ( !isLegal ) { - BadWordInfo bwi = {0}; - BoardCtxt* board = (BoardCtxt*)closure; - XP_S16 turn = server_getCurrentTurn( board->server, NULL ); - - bwi.nWords = 1; - bwi.words[0] = word; - bwi.dictName = - dict_getShortName( model_getPlayerDict( board->model, turn ) ); - - ok = !board->badWordRejected - && util_warnIllegalWord( board->util, &bwi, turn, XP_FALSE ); - board->badWordRejected = !ok || board->badWordRejected; + BadWordList* bwlp = (BadWordList*)closure; + bwlp->bwi.words[bwlp->bwi.nWords] = &bwlp->buf[bwlp->index]; + XP_STRCAT( &bwlp->buf[bwlp->index], word ); + bwlp->index += XP_STRLEN(word) + 1; + ++bwlp->bwi.nWords; } -} /* warnBadWords */ +} /* saveBadWords */ static void boardNotifyTrade( BoardCtxt* board, const TrayTileSet* tiles ) @@ -1025,7 +1023,8 @@ boardNotifyTrade( BoardCtxt* board, const TrayTileSet* tiles ) } XP_Bool -board_commitTurn( BoardCtxt* board, XP_Bool alreadyConfirmed ) +board_commitTurn( BoardCtxt* board, XP_Bool phoniesConfirmed, + XP_Bool turnConfirmed /* includes trade */ ) { XP_Bool result = XP_FALSE; const XP_S16 turn = server_getCurrentTurn( board->server, NULL ); @@ -1038,7 +1037,7 @@ board_commitTurn( BoardCtxt* board, XP_Bool alreadyConfirmed ) } else if ( 0 == model_getNumTilesTotal( board->model, turn ) ) { /* game's over but still undoable so turn hasn't changed; do nothing */ - } else if ( alreadyConfirmed || checkRevealTray( board ) ) { + } else if ( phoniesConfirmed || turnConfirmed || checkRevealTray( board ) ) { if ( pti->tradeInProgress ) { TileBit traySelBits = pti->traySelBits; result = XP_TRUE; /* there's at least the window to clean up @@ -1049,7 +1048,7 @@ board_commitTurn( BoardCtxt* board, XP_Bool alreadyConfirmed ) } else { TrayTileSet selTiles; getSelTiles( board, traySelBits, &selTiles ); - if ( alreadyConfirmed ) { + if ( turnConfirmed ) { /* server_commitTrade() changes selPlayer, so board_endTrade must be called first() */ (void)board_endTrade( board ); @@ -1061,11 +1060,10 @@ board_commitTurn( BoardCtxt* board, XP_Bool alreadyConfirmed ) } } else { XWStreamCtxt* stream = NULL; - XP_Bool legal = alreadyConfirmed; + XP_Bool legal = turnConfirmed; + BadWordList bwl = {0}; if ( !legal ) { - WordNotifierInfo info; - XP_Bool warn; stream = mem_stream_make( MPPARM(board->mpool) util_getVTManager(board->util), NULL, CHANNEL_NONE, (MemStreamCloseCallback)NULL ); @@ -1074,27 +1072,30 @@ board_commitTurn( BoardCtxt* board, XP_Bool alreadyConfirmed ) STR_COMMIT_CONFIRM); stream_catString( stream, str ); - warn = board->util->gameInfo->phoniesAction == PHONIES_WARN; - - board->badWordRejected = XP_FALSE; - info.proc = warnBadWords; - info.closure = board; + XP_Bool warn = board->util->gameInfo->phoniesAction == PHONIES_WARN; + WordNotifierInfo info; + if ( warn ) { + info.proc = saveBadWords; + info.closure = &bwl; + } legal = model_checkMoveLegal( board->model, turn, stream, - warn? &info:(WordNotifierInfo*)NULL); + warn? &info:(WordNotifierInfo*)NULL); } - if ( !legal || board->badWordRejected ) { - result = XP_FALSE; + if ( 0 < bwl.bwi.nWords && !phoniesConfirmed ) { + bwl.bwi.dictName = + dict_getShortName( model_getPlayerDict( board->model, turn ) ); + util_notifyIllegalWords( board->util, &bwl.bwi, turn, XP_FALSE ); } else { /* Hide the tray so no peeking. Leave it hidden even if user cancels as otherwise another player could get around passwords and peek at tiles. */ - if ( !alreadyConfirmed + if ( !turnConfirmed && gi_countLocalPlayers( board->gi, XP_TRUE ) > 1 ) { result = board_hideTray( board ); } - if ( board->skipCommitConfirm || alreadyConfirmed ) { + if ( board->skipCommitConfirm || turnConfirmed ) { result = server_commitMove( board->server ) || result; /* invalidate all tiles in case we'll be drawing this tray again rather than some other -- as is the case in a diff --git a/xwords4/common/board.h b/xwords4/common/board.h index 4019eeb57..e81cb2d03 100644 --- a/xwords4/common/board.h +++ b/xwords4/common/board.h @@ -176,7 +176,8 @@ XP_Bool board_setBlankValue( BoardCtxt* board, XP_U16 XP_UNUSED(player), void board_resetEngine( BoardCtxt* board ); -XP_Bool board_commitTurn( BoardCtxt* board, XP_Bool alreadyConfirmed ); +XP_Bool board_commitTurn( BoardCtxt* board, XP_Bool phoniesConfirmed, + XP_Bool turnConfirmed ); void board_pushTimerSave( BoardCtxt* board ); void board_popTimerSave( BoardCtxt* board ); diff --git a/xwords4/common/boardp.h b/xwords4/common/boardp.h index bd6f2f9fc..f3d3db64f 100644 --- a/xwords4/common/boardp.h +++ b/xwords4/common/boardp.h @@ -169,7 +169,6 @@ struct BoardCtxt { XP_Bool showGrid; XP_Bool gameOver; XP_Bool leftHanded; - XP_Bool badWordRejected; XP_Bool timerPending; XP_Bool disableArrow; XP_Bool hideValsInTray; diff --git a/xwords4/common/server.c b/xwords4/common/server.c index c69438c95..87f8cf02a 100644 --- a/xwords4/common/server.c +++ b/xwords4/common/server.c @@ -1618,7 +1618,7 @@ badWordMoveUndoAndTellUser( ServerCtxt* server, BadWordInfo* bwi ) model_rejectPreviousMove( model, server->pool, &turn ); - util_warnIllegalWord( server->vol.util, bwi, turn, XP_TRUE ); + util_notifyIllegalWords( server->vol.util, bwi, turn, XP_TRUE ); } /* badWordMoveUndoAndTellUser */ EngineCtxt* diff --git a/xwords4/common/tray.c b/xwords4/common/tray.c index 251411399..64b518c37 100644 --- a/xwords4/common/tray.c +++ b/xwords4/common/tray.c @@ -460,7 +460,7 @@ handleActionInTray( BoardCtxt* board, XP_S16 index, XP_Bool onDivider ) } #endif } else if ( index == -(MAX_TRAY_TILES) ) { /* pending score tile */ - result = board_commitTurn( board, XP_FALSE ); + result = board_commitTurn( board, XP_FALSE, XP_FALSE ); #if defined XWFEATURE_TRAYUNDO_ALL } else if ( index < 0 ) { /* other empty area */ /* it better be true */ diff --git a/xwords4/common/util.h b/xwords4/common/util.h index 1c1c76c7e..abfaff6b5 100644 --- a/xwords4/common/util.h +++ b/xwords4/common/util.h @@ -159,7 +159,7 @@ typedef struct UtilVtable { XP_U16 stringCode, XP_U16 quantity ); - XP_Bool (*m_util_warnIllegalWord)( XW_UtilCtxt* uc, BadWordInfo* bwi, + void (*m_util_notifyIllegalWords)( XW_UtilCtxt* uc, BadWordInfo* bwi, XP_U16 turn, XP_Bool turnLost ); void (*m_util_remSelected)(XW_UtilCtxt* uc); @@ -302,8 +302,8 @@ struct XW_UtilCtxt { #define util_getUserQuantityString( uc, c, q ) \ (uc)->vtable->m_util_getUserQuantityString((uc),(c),(q)) -#define util_warnIllegalWord( uc, w, p, b ) \ - (uc)->vtable->m_util_warnIllegalWord((uc),(w),(p),(b)) +#define util_notifyIllegalWords( uc, w, p, b ) \ + (uc)->vtable->m_util_notifyIllegalWords((uc),(w),(p),(b)) #define util_remSelected( uc ) \ (uc)->vtable->m_util_remSelected((uc)) diff --git a/xwords4/linux/cursesmain.c b/xwords4/linux/cursesmain.c index 92405c87a..9432f9080 100644 --- a/xwords4/linux/cursesmain.c +++ b/xwords4/linux/cursesmain.c @@ -620,7 +620,7 @@ static XP_Bool handleCommit( CursesAppGlobals* globals ) { globals->doDraw = board_commitTurn( globals->cGlobals.game.board, - XP_FALSE ); + XP_FALSE, XP_FALSE ); return XP_TRUE; } /* handleCommit */ @@ -1371,15 +1371,14 @@ curses_util_turnChanged( XW_UtilCtxt* XP_UNUSED(uc), XP_S16 newTurn ) } #endif -static XP_Bool -curses_util_warnIllegalWord( XW_UtilCtxt* XP_UNUSED(uc), - BadWordInfo* XP_UNUSED(bwi), - XP_U16 XP_UNUSED(player), - XP_Bool XP_UNUSED(turnLost) ) +static void +curses_util_notifyIllegalWords( XW_UtilCtxt* XP_UNUSED(uc), + BadWordInfo* XP_UNUSED(bwi), + XP_U16 XP_UNUSED(player), + XP_Bool XP_UNUSED(turnLost) ) { - XP_WARNF( "curses_util_warnIllegalWord not implemented" ); - return XP_FALSE; -} /* curses_util_warnIllegalWord */ + XP_WARNF( "curses_util_notifyIllegalWords not implemented" ); +} /* curses_util_notifyIllegalWord */ static void curses_util_remSelected( XW_UtilCtxt* uc ) @@ -1445,7 +1444,7 @@ setupCursesUtilCallbacks( CursesAppGlobals* globals, XW_UtilCtxt* util ) #ifdef XWFEATURE_TURNCHANGENOTIFY util->vtable->m_util_turnChanged = curses_util_turnChanged; #endif - util->vtable->m_util_warnIllegalWord = curses_util_warnIllegalWord; + util->vtable->m_util_notifyIllegalWords = curses_util_notifyIllegalWords; util->vtable->m_util_remSelected = curses_util_remSelected; #ifndef XWFEATURE_STANDALONE_ONLY util->vtable->m_util_makeStreamFromAddr = curses_util_makeStreamFromAddr; diff --git a/xwords4/linux/gtkboard.c b/xwords4/linux/gtkboard.c index 1406593af..ecb5f6385 100644 --- a/xwords4/linux/gtkboard.c +++ b/xwords4/linux/gtkboard.c @@ -1445,7 +1445,7 @@ handle_trade_button( GtkWidget* XP_UNUSED(widget), GtkGameGlobals* globals ) static void handle_done_button( GtkWidget* XP_UNUSED(widget), GtkGameGlobals* globals ) { - if ( board_commitTurn( globals->cGlobals.game.board, XP_FALSE ) ) { + if ( board_commitTurn( globals->cGlobals.game.board, XP_FALSE, XP_FALSE ) ) { board_draw( globals->cGlobals.game.board ); disenable_buttons( globals ); } @@ -1543,7 +1543,7 @@ handle_hide_button( GtkWidget* XP_UNUSED(widget), GtkGameGlobals* globals ) static void handle_commit_button( GtkWidget* XP_UNUSED(widget), GtkGameGlobals* globals ) { - if ( board_commitTurn( globals->cGlobals.game.board, XP_FALSE ) ) { + if ( board_commitTurn( globals->cGlobals.game.board, XP_FALSE, XP_FALSE ) ) { board_draw( globals->cGlobals.game.board ); } } /* handle_commit_button */ @@ -2117,49 +2117,48 @@ gtk_util_requestTime( XW_UtilCtxt* uc ) globals->idleID = g_idle_add( idle_func, globals ); } /* gtk_util_requestTime */ -static XP_Bool -gtk_util_warnIllegalWord( XW_UtilCtxt* uc, BadWordInfo* bwi, XP_U16 player, - XP_Bool turnLost ) +static gint +ask_bad_words( gpointer data ) +{ + GtkGameGlobals* globals = (GtkGameGlobals*)data; + CommonGlobals* cGlobals = &globals->cGlobals; + + if ( GTK_RESPONSE_YES == gtkask( globals->window, cGlobals->question, + GTK_BUTTONS_YES_NO, NULL ) ) { + board_commitTurn( cGlobals->game.board, XP_TRUE, XP_FALSE ); + } + return 0; +} + +static void +gtk_util_notifyIllegalWords( XW_UtilCtxt* uc, BadWordInfo* bwi, XP_U16 player, + XP_Bool turnLost ) { - XP_Bool result; GtkGameGlobals* globals = (GtkGameGlobals*)uc->closure; + CommonGlobals* cGlobals = &globals->cGlobals; char buf[300]; + gchar* strs = g_strjoinv( "\", \"", (gchar**)bwi->words ); if ( turnLost ) { - char wordsBuf[256]; - XP_U16 i; - XP_UCHAR* name = globals->cGlobals.gi->players[player].name; + XP_UCHAR* name = cGlobals->gi->players[player].name; XP_ASSERT( !!name ); - for ( i = 0, wordsBuf[0] = '\0'; ; ) { - char wordBuf[18]; - sprintf( wordBuf, "\"%s\"", bwi->words[i] ); - strcat( wordsBuf, wordBuf ); - if ( ++i == bwi->nWords ) { - break; - } - strcat( wordsBuf, ", " ); - } - sprintf( buf, "Player %d (%s) played illegal word[s] %s; loses turn.", - player+1, name, wordsBuf ); + player+1, name, strs ); - if ( globals->cGlobals.params->skipWarnings ) { + if ( cGlobals->params->skipWarnings ) { XP_LOGF( "%s", buf ); } else { gtkUserError( globals, buf ); } - result = XP_TRUE; } else { - XP_ASSERT( bwi->nWords == 1 ); - sprintf( buf, "Word \"%s\" not in the current dictionary (%s). " - "Use it anyway?", bwi->words[0], bwi->dictName ); - result = GTK_RESPONSE_YES == gtkask( globals->window, buf, - GTK_BUTTONS_YES_NO, NULL ); - } + sprintf( cGlobals->question, "Word[s] \"%s\" not in the current dictionary (%s). " + "Use anyway?", strs, bwi->dictName ); - return result; -} /* gtk_util_warnIllegalWord */ + (void)g_idle_add( ask_bad_words, globals ); + } + g_free( strs ); +} /* gtk_util_notifyIllegalWords */ static void gtk_util_remSelected( XW_UtilCtxt* uc ) @@ -2276,7 +2275,7 @@ ask_move( gpointer data ) gint chosen = gtkask( globals->window, cGlobals->question, buttons, NULL ); if ( GTK_RESPONSE_OK == chosen || chosen == GTK_RESPONSE_YES ) { BoardCtxt* board = cGlobals->game.board; - if ( board_commitTurn( board, XP_TRUE ) ) { + if ( board_commitTurn( board, XP_TRUE, XP_TRUE ) ) { board_draw( board ); } } @@ -2315,7 +2314,7 @@ ask_trade( gpointer data ) cGlobals->question, GTK_BUTTONS_YES_NO, NULL ) ) { BoardCtxt* board = cGlobals->game.board; - if ( board_commitTurn( board, XP_TRUE ) ) { + if ( board_commitTurn( board, XP_TRUE, XP_TRUE ) ) { board_draw( board ); } } @@ -2488,7 +2487,7 @@ setupGtkUtilCallbacks( GtkGameGlobals* globals, XW_UtilCtxt* util ) util->vtable->m_util_setTimer = gtk_util_setTimer; util->vtable->m_util_clearTimer = gtk_util_clearTimer; util->vtable->m_util_requestTime = gtk_util_requestTime; - util->vtable->m_util_warnIllegalWord = gtk_util_warnIllegalWord; + util->vtable->m_util_notifyIllegalWords = gtk_util_notifyIllegalWords; util->vtable->m_util_remSelected = gtk_util_remSelected; #ifndef XWFEATURE_STANDALONE_ONLY util->vtable->m_util_makeStreamFromAddr = gtk_util_makeStreamFromAddr;