diff --git a/xwords4/android/XWords4/jni/Android.mk b/xwords4/android/XWords4/jni/Android.mk index b1aabc550..a5ab9830f 100644 --- a/xwords4/android/XWords4/jni/Android.mk +++ b/xwords4/android/XWords4/jni/Android.mk @@ -25,6 +25,7 @@ local_DEFINES += \ -DDISABLE_TILE_SEL \ -DXWFEATURE_BOARDWORDS \ -DXWFEATURE_WALKDICT \ + -DFEATURE_TRAY_EDIT \ -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 cd711ec54..ff471ed14 100644 --- a/xwords4/android/XWords4/jni/utilwrapper.c +++ b/xwords4/android/XWords4/jni/utilwrapper.c @@ -145,17 +145,13 @@ and_util_confirmTrade( XW_UtilCtxt* uc, const XP_UCHAR** tiles, XP_U16 nTiles ) } static XP_S16 -and_util_userPickTile( XW_UtilCtxt* uc, const PickInfo* pi, - XP_U16 playerNum, const XP_UCHAR** texts, XP_U16 nTiles ) +and_util_userPickTileBlank( XW_UtilCtxt* uc, XP_U16 playerNum, + const XP_UCHAR** tileFaces, XP_U16 nTiles ) { XP_S16 result = -1; - UTIL_CBK_HEADER("userPickTile", "(I[Ljava/lang/String;)I" ); + UTIL_CBK_HEADER("userPickTileBlank", "(I[Ljava/lang/String;)I" ); -#ifdef FEATURE_TRAY_EDIT - ++error; /* need to pass pi if this is on */ -#endif - - jobject jtexts = makeStringArray( env, nTiles, texts ); + jobject jtexts = makeStringArray( env, nTiles, tileFaces ); result = (*env)->CallIntMethod( env, util->jutil, mid, playerNum, jtexts ); @@ -163,8 +159,27 @@ and_util_userPickTile( XW_UtilCtxt* uc, const PickInfo* pi, (*env)->DeleteLocalRef( env, jtexts ); UTIL_CBK_TAIL(); return result; -} /* and_util_userPickTile */ +} +static XP_S16 +and_util_userPickTileTray( XW_UtilCtxt* uc, const PickInfo* pi, + XP_U16 playerNum, const XP_UCHAR** tileFaces, + XP_U16 nTiles ) +{ + XP_S16 result = -1; + UTIL_CBK_HEADER("userPickTileTray", + "(I[Ljava/lang/String;[Ljava/lang/String;I)I" ); + jobject jtexts = makeStringArray( env, nTiles, tileFaces ); + jobject jcurtiles = makeStringArray( env, pi->nCurTiles, pi->curTiles ); + result = (*env)->CallIntMethod( env, util->jutil, mid, + playerNum, jtexts, jcurtiles, + pi->thisPick ); + (*env)->DeleteLocalRef( env, jtexts ); + (*env)->DeleteLocalRef( env, jcurtiles ); + + UTIL_CBK_TAIL(); + return result; +} /* and_util_userPickTile */ static XP_Bool and_util_askPassword( XW_UtilCtxt* uc, const XP_UCHAR* name, @@ -511,7 +526,8 @@ makeUtil( MPFORMAL JNIEnv** envp, jobject jutil, CurGameInfo* gi, SET_PROC(userError); SET_PROC(userQuery); SET_PROC(confirmTrade); - SET_PROC(userPickTile); + SET_PROC(userPickTileBlank); + SET_PROC(userPickTileTray); SET_PROC(askPassword); SET_PROC(trayHiddenChange); SET_PROC(yOffsetChange); diff --git a/xwords4/android/XWords4/res/layout/game_config.xml b/xwords4/android/XWords4/res/layout/game_config.xml index f8e0744e7..fe92d5d5c 100644 --- a/xwords4/android/XWords4/res/layout/game_config.xml +++ b/xwords4/android/XWords4/res/layout/game_config.xml @@ -217,6 +217,11 @@ android:entries="@array/phony_names" /> + diff --git a/xwords4/android/XWords4/res/values/strings.xml b/xwords4/android/XWords4/res/values/strings.xml index c6d6cda4d..e16bf6ce8 100644 --- a/xwords4/android/XWords4/res/values/strings.xml +++ b/xwords4/android/XWords4/res/values/strings.xml @@ -1785,6 +1785,10 @@ Find First letters + Undo last + Pick for me + Tile picker\n(so far: %s) + Pick tiles face-up diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java index e36cc4cc8..6ad54c243 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java @@ -65,13 +65,15 @@ public class BoardActivity extends XWActivity private static final int DLG_BADWORDS = DLG_OKONLY + 1; private static final int QUERY_REQUEST_BLK = DLG_OKONLY + 2; private static final int QUERY_INFORM_BLK = DLG_OKONLY + 3; - private static final int PICK_TILE_REQUEST_BLK = DLG_OKONLY + 4; + private static final int PICK_TILE_REQUESTBLANK_BLK = DLG_OKONLY + 4; private static final int ASK_PASSWORD_BLK = DLG_OKONLY + 5; private static final int DLG_RETRY = DLG_OKONLY + 6; private static final int QUERY_ENDGAME = DLG_OKONLY + 7; private static final int DLG_DELETED = DLG_OKONLY + 8; private static final int DLG_INVITE = DLG_OKONLY + 9; private static final int DLG_SCORES_BLK = DLG_OKONLY + 10; + private static final int DLG_LOOKUP = DLG_OKONLY + 11; + private static final int PICK_TILE_REQUESTTRAY_BLK = DLG_OKONLY + 12; private static final int CHAT_REQUEST = 1; private static final int SCREEN_ON_TIME = 10 * 60 * 1000; // 10 mins @@ -121,6 +123,8 @@ public class BoardActivity extends XWActivity private int m_dlgTitle; private String m_dlgTitleStr; private String[] m_texts; + private String m_curTiles; + private boolean m_canUndoTiles; private boolean m_firingPrefs; private JNIUtils m_jniu; private boolean m_volKeysZoom; @@ -163,7 +167,7 @@ public class BoardActivity extends XWActivity } @Override - protected Dialog onCreateDialog( int id ) + protected Dialog onCreateDialog( final int id ) { Dialog dialog = super.onCreateDialog( id ); if ( null == dialog ) { @@ -263,9 +267,9 @@ public class BoardActivity extends XWActivity dialog.setOnDismissListener( makeODLforBlocking( id ) ); break; - case PICK_TILE_REQUEST_BLK: - ab = new AlertDialog.Builder( this ) - .setTitle( R.string.title_tile_picker ); + case PICK_TILE_REQUESTBLANK_BLK: + case PICK_TILE_REQUESTTRAY_BLK: + ab = new AlertDialog.Builder( this ); lstnr = new DialogInterface.OnClickListener() { public void onClick( DialogInterface dialog, int item ) { @@ -274,6 +278,34 @@ public class BoardActivity extends XWActivity }; ab.setItems( m_texts, lstnr ); + if ( PICK_TILE_REQUESTBLANK_BLK == id ) { + ab.setTitle( R.string.title_tile_picker ); + } else { + ab.setTitle( Utils.format( this, R.string.cur_tilesf, + m_curTiles ) ); + if ( m_canUndoTiles ) { + DialogInterface.OnClickListener undoClicked = + new DialogInterface.OnClickListener() { + public void onClick( DialogInterface dialog, + int whichButton ) { + m_resultCode = UtilCtxt.PICKER_BACKUP; + removeDialog( id ); + } + }; + ab.setPositiveButton( R.string.tilepick_undo, + undoClicked ); + } + DialogInterface.OnClickListener doAllClicked = + new DialogInterface.OnClickListener() { + public void onClick( DialogInterface dialog, + int whichButton ) { + m_resultCode = UtilCtxt.PICKER_PICKALL; + removeDialog( id ); + } + }; + ab.setNegativeButton( R.string.tilepick_all, doAllClicked ); + } + dialog = ab.create(); dialog.setOnDismissListener( makeODLforBlocking( id ) ); break; @@ -1043,10 +1075,22 @@ public class BoardActivity extends XWActivity // This is supposed to be called from the jni thread @Override - public int userPickTile( int playerNum, String[] texts ) + public int userPickTileBlank( int playerNum, String[] texts) { m_texts = texts; - waitBlockingDialog( PICK_TILE_REQUEST_BLK, 0 ); + waitBlockingDialog( PICK_TILE_REQUESTBLANK_BLK, 0 ); + return m_resultCode; + } + + @Override + public int userPickTileTray( int playerNum, String[] texts, + String[] curTiles, int nPicked ) + { + m_texts = texts; + m_curTiles = TextUtils.join( ", ", curTiles ); + m_canUndoTiles = 0 < nPicked; + waitBlockingDialog( PICK_TILE_REQUESTTRAY_BLK, + UtilCtxt.PICKER_PICKALL ); return m_resultCode; } diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameConfig.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameConfig.java index 192ade296..4c3e88916 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameConfig.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameConfig.java @@ -114,6 +114,7 @@ public class GameConfig extends XWActivity ,R.id.room_spinner ,R.id.refresh_button ,R.id.hints_allowed + ,R.id.pick_faceup ,R.id.use_timer ,R.id.timer_minutes_edit ,R.id.smart_robot @@ -514,7 +515,10 @@ public class GameConfig extends XWActivity setSmartnessSpinner(); - Utils.setChecked( this, R.id.hints_allowed, !m_gi.hintsNotAllowed ); + Utils.setChecked( this, R.id.hints_allowed, + !m_gi.hintsNotAllowed ); + Utils.setChecked( this, R.id.pick_faceup, + m_gi.allowPickTiles ); Utils.setInt( this, R.id.timer_minutes_edit, m_gi.gameSeconds/60/m_gi.nPlayers ); @@ -950,6 +954,7 @@ public class GameConfig extends XWActivity private void saveChanges() { m_gi.hintsNotAllowed = !Utils.getChecked( this, R.id.hints_allowed ); + m_gi.allowPickTiles = Utils.getChecked( this, R.id.pick_faceup ); m_gi.timerEnabled = Utils.getChecked( this, R.id.use_timer ); m_gi.gameSeconds = 60 * m_gi.nPlayers * Utils.getInt( this, R.id.timer_minutes_edit ); 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 c88d47cd5..ecccfa073 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 @@ -27,8 +27,13 @@ public interface UtilCtxt { static final int BONUS_TRIPLE_LETTER = 3; static final int BONUS_TRIPLE_WORD = 4; - int userPickTile( /* PickInfo* pi, add once tile-picking is enabled */ - int playerNum, String[] texts ); + // must match defns in util.h + public static final int PICKER_PICKALL = -1; + public static final int PICKER_BACKUP = -2; + + int userPickTileBlank( int playerNum, String[] texts ); + int userPickTileTray( int playerNum, String[] tiles, + String[] curTiles, int nPicked ); String askPassword( String name ); void turnChanged(); 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 53d525c50..5a733f140 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 @@ -40,9 +40,16 @@ public class UtilCtxtImpl implements UtilCtxt { subclassOverride( "requestTime" ); } - public int userPickTile( int playerNum, String[] texts ) + public int userPickTileBlank( int playerNum, String[] texts ) { - subclassOverride( "userPickTile" ); + subclassOverride( "userPickTileBlank" ); + return 0; + } + + public int userPickTileTray( int playerNum, String[] texts, + String[] curTiles, int nPicked ) + { + subclassOverride( "userPickTileTray" ); return 0; } diff --git a/xwords4/common/model.c b/xwords4/common/model.c index ea8d0b6fe..6c2106048 100644 --- a/xwords4/common/model.c +++ b/xwords4/common/model.c @@ -1095,17 +1095,11 @@ askBlankTile( ModelCtxt* model, XP_U16 turn ) XP_S16 chosen; const XP_UCHAR* tfaces[MAX_UNIQUE_TILES]; Tile tiles[MAX_UNIQUE_TILES]; - PickInfo pi; - - pi.why = PICK_FOR_BLANK; - pi.nTotal = 1; - pi.thisPick = 1; model_packTilesUtil( model, NULL, XP_FALSE, &nUsed, tfaces, tiles ); - chosen = util_userPickTile( model->vol.util, &pi, - turn, tfaces, nUsed ); + chosen = util_userPickTileBlank( model->vol.util, turn, tfaces, nUsed ); if ( chosen < 0 ) { chosen = 0; diff --git a/xwords4/common/server.c b/xwords4/common/server.c index 1c43e59ba..774726abe 100644 --- a/xwords4/common/server.c +++ b/xwords4/common/server.c @@ -1547,7 +1547,6 @@ fetchTiles( ServerCtxt* server, XP_U16 playerNum, XP_U16 nToFetch, oneTile.nTiles = 1; - pi.why = PICK_FOR_CHEAT; pi.nTotal = nToFetch; pi.thisPick = 0; pi.curTiles = curTray; @@ -1565,8 +1564,8 @@ fetchTiles( ServerCtxt* server, XP_U16 playerNum, XP_U16 nToFetch, model_packTilesUtil( server->vol.model, pool, XP_TRUE, &nUsed, texts, tiles ); - chosen = util_userPickTile( server->vol.util, &pi, playerNum, - texts, nUsed ); + chosen = util_userPickTileTray( server->vol.util, &pi, playerNum, + texts, nUsed ); if ( chosen == PICKER_PICKALL ) { ask = XP_FALSE; diff --git a/xwords4/common/util.h b/xwords4/common/util.h index 399fe12b1..dc25d28da 100644 --- a/xwords4/common/util.h +++ b/xwords4/common/util.h @@ -70,11 +70,6 @@ typedef enum { QUERY_LAST_COMMON } UtilQueryID; -typedef enum { - PICK_FOR_BLANK - , PICK_FOR_CHEAT -} PICK_WHY; - #define PICKER_PICKALL -1 #define PICKER_BACKUP -2 @@ -83,7 +78,6 @@ typedef struct PickInfo { XP_U16 nCurTiles; XP_U16 nTotal; /* count to fetch for turn, <= MAX_TRAY_TILES */ XP_U16 thisPick; /* <= nTotal */ - PICK_WHY why; } PickInfo; typedef struct BadWordInfo { @@ -115,9 +109,12 @@ typedef struct UtilVtable { XP_Bool (*m_util_confirmTrade)( XW_UtilCtxt* uc, const XP_UCHAR** tiles, XP_U16 nTiles ); /* return of < 0 means computer should pick */ - XP_S16 (*m_util_userPickTile)( XW_UtilCtxt* uc, const PickInfo* pi, - XP_U16 playerNum, - const XP_UCHAR** texts, XP_U16 nTiles ); + XP_S16 (*m_util_userPickTileBlank)( XW_UtilCtxt* uc, XP_U16 playerNum, + const XP_UCHAR** tileFaces, + XP_U16 nTiles ); + XP_S16 (*m_util_userPickTileTray)( XW_UtilCtxt* uc, const PickInfo* pi, + XP_U16 playerNum, + const XP_UCHAR** texts, XP_U16 nTiles ); XP_Bool (*m_util_askPassword)( XW_UtilCtxt* uc, const XP_UCHAR* name, XP_UCHAR* buf, XP_U16* len ); @@ -216,8 +213,10 @@ struct XW_UtilCtxt { #define util_confirmTrade( uc, tx, nt ) \ (uc)->vtable->m_util_confirmTrade((uc),(tx),(nt)) -#define util_userPickTile( uc, w, n, tx, nt ) \ - (uc)->vtable->m_util_userPickTile( (uc), (w), (n), (tx), (nt) ) +#define util_userPickTileBlank( uc, n, tx, nt ) \ + (uc)->vtable->m_util_userPickTileBlank( (uc), (n), (tx), (nt) ) +#define util_userPickTileTray( uc, w, n, tx, nt ) \ + (uc)->vtable->m_util_userPickTileTray( (uc), (w), (n), (tx), (nt) ) #define util_askPassword( uc, n, b, lp ) \ (uc)->vtable->m_util_askPassword( (uc), (n), (b), (lp) ) diff --git a/xwords4/linux/cursesmain.c b/xwords4/linux/cursesmain.c index 56c64610f..a032812a1 100644 --- a/xwords4/linux/cursesmain.c +++ b/xwords4/linux/cursesmain.c @@ -228,9 +228,26 @@ cursesUserError( CursesAppGlobals* globals, const char* format, ... ) } /* cursesUserError */ static XP_S16 -curses_util_userPickTile( XW_UtilCtxt* uc, const PickInfo* XP_UNUSED(pi), - XP_U16 playerNum, const XP_UCHAR** texts, - XP_U16 nTiles ) +curses_util_userPickTileBlank( XW_UtilCtxt* uc, XP_U16 playerNum, + const XP_UCHAR** texts, XP_U16 nTiles ) +{ + CursesAppGlobals* globals = (CursesAppGlobals*)uc->closure; + char query[128]; + XP_S16 index; + char* playerName = globals->cGlobals.params->gi.players[playerNum].name; + + snprintf( query, sizeof(query), + "Pick tile for %s! (Tab or type letter to select " + "then hit .)", playerName ); + + index = curses_askLetter( globals, query, texts, nTiles ); + return index; +} /* util_userPickTile */ + +static XP_S16 +curses_util_userPickTileTray( XW_UtilCtxt* uc, const PickInfo* XP_UNUSED(pi), + XP_U16 playerNum, const XP_UCHAR** texts, + XP_U16 nTiles ) { CursesAppGlobals* globals = (CursesAppGlobals*)uc->closure; char query[128]; @@ -1478,7 +1495,8 @@ setupCursesUtilCallbacks( CursesAppGlobals* globals, XW_UtilCtxt* util ) #endif util->vtable->m_util_userQuery = curses_util_userQuery; util->vtable->m_util_confirmTrade = curses_util_confirmTrade; - util->vtable->m_util_userPickTile = curses_util_userPickTile; + util->vtable->m_util_userPickTileBlank = curses_util_userPickTileBlank; + util->vtable->m_util_userPickTileTray = curses_util_userPickTileTray; util->vtable->m_util_trayHiddenChange = curses_util_trayHiddenChange; util->vtable->m_util_informMove = curses_util_informMove; util->vtable->m_util_notifyGameOver = curses_util_notifyGameOver; diff --git a/xwords4/linux/gtkletterask.c b/xwords4/linux/gtkletterask.c index c744b7563..20a99b020 100644 --- a/xwords4/linux/gtkletterask.c +++ b/xwords4/linux/gtkletterask.c @@ -42,7 +42,7 @@ abort_button_event( GtkWidget* XP_UNUSED(widget), gpointer XP_UNUSED(closure) ) #define BUTTONS_PER_ROW 13 XP_S16 -gtkletterask( const PickInfo* pi, const XP_UCHAR* name, +gtkletterask( const PickInfo* pi, XP_Bool forTray, const XP_UCHAR* name, XP_U16 nTiles, const XP_UCHAR** texts ) { GtkWidget* dialog; @@ -103,19 +103,18 @@ gtkletterask( const PickInfo* pi, const XP_UCHAR* name, dialog = gtk_dialog_new(); gtk_window_set_modal( GTK_WINDOW( dialog ), TRUE ); - XP_Bool forBlank = PICK_FOR_BLANK == pi->why; - if ( forBlank ) { - txt = "Choose a letter for your blank."; - } else { + if ( forTray ) { char* fmt = "Choose a tile for %s."; XP_SNPRINTF( buf, sizeof(buf), fmt, name ); txt = buf; + } else { + txt = "Choose a letter for your blank."; } label = gtk_label_new( txt ); gtk_container_add (GTK_CONTAINER (GTK_DIALOG(dialog)->vbox), label); - if ( !forBlank ) { + if ( forTray ) { char curTilesBuf[64]; int len = snprintf( curTilesBuf, sizeof(curTilesBuf), "%s", "Tiles so far: " ); diff --git a/xwords4/linux/gtkletterask.h b/xwords4/linux/gtkletterask.h index 1d38a0cbf..ff7c3f02b 100644 --- a/xwords4/linux/gtkletterask.h +++ b/xwords4/linux/gtkletterask.h @@ -26,7 +26,8 @@ #include "gtkmain.h" -XP_S16 gtkletterask( const PickInfo* pi, const XP_UCHAR* name, +XP_S16 gtkletterask( const PickInfo* pi, XP_Bool forTray, + const XP_UCHAR* name, XP_U16 nTiles, const XP_UCHAR** texts ); diff --git a/xwords4/linux/gtkmain.c b/xwords4/linux/gtkmain.c index 1cae454c6..dcb995a4c 100644 --- a/xwords4/linux/gtkmain.c +++ b/xwords4/linux/gtkmain.c @@ -1300,15 +1300,27 @@ gtk_util_getVTManager(XW_UtilCtxt* uc) } /* linux_util_getVTManager */ static XP_S16 -gtk_util_userPickTile( XW_UtilCtxt* uc, const PickInfo* pi, - XP_U16 playerNum, const XP_UCHAR** texts, - XP_U16 nTiles ) +gtk_util_userPickTileBlank( XW_UtilCtxt* uc, XP_U16 playerNum, + const XP_UCHAR** texts, XP_U16 nTiles ) { XP_S16 chosen; GtkAppGlobals* globals = (GtkAppGlobals*)uc->closure; XP_UCHAR* name = globals->cGlobals.params->gi.players[playerNum].name; - chosen = gtkletterask( pi, name, nTiles, texts ); + chosen = gtkletterask( NULL, XP_FALSE, name, nTiles, texts ); + return chosen; +} + +static XP_S16 +gtk_util_userPickTileTray( XW_UtilCtxt* uc, const PickInfo* pi, + XP_U16 playerNum, const XP_UCHAR** texts, + XP_U16 nTiles ) +{ + XP_S16 chosen; + GtkAppGlobals* globals = (GtkAppGlobals*)uc->closure; + XP_UCHAR* name = globals->cGlobals.params->gi.players[playerNum].name; + + chosen = gtkletterask( pi, XP_TRUE, name, nTiles, texts ); return chosen; } /* gtk_util_userPickTile */ @@ -1930,7 +1942,8 @@ setupGtkUtilCallbacks( GtkAppGlobals* globals, XW_UtilCtxt* util ) util->vtable->m_util_userQuery = gtk_util_userQuery; util->vtable->m_util_confirmTrade = gtk_util_confirmTrade; util->vtable->m_util_getVTManager = gtk_util_getVTManager; - util->vtable->m_util_userPickTile = gtk_util_userPickTile; + util->vtable->m_util_userPickTileBlank = gtk_util_userPickTileBlank; + util->vtable->m_util_userPickTileTray = gtk_util_userPickTileTray; util->vtable->m_util_askPassword = gtk_util_askPassword; util->vtable->m_util_trayHiddenChange = gtk_util_trayHiddenChange; util->vtable->m_util_yOffsetChange = gtk_util_yOffsetChange;