Merge tag 'android_beta_111' into android_release

new release
This commit is contained in:
Eric House 2016-10-25 08:34:50 -07:00
commit c80665bf41
33 changed files with 252 additions and 157 deletions

View file

@ -48,3 +48,4 @@ chat_row.xml
fragact.xml
main.xml
dualcontainer.xml
dict_browser_list.xml

View file

@ -22,7 +22,7 @@
to come from a domain that you own or have control over. -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.eehouse.android.xw4"
android:versionCode="105"
android:versionCode="106"
android:versionName="@string/app_version"
>

View file

@ -2014,7 +2014,6 @@ XLATE-ME
/** <string name="data_gsm_only">SMS Data is only available on GSM phones.</string>
*/
public static final int data_gsm_only=0x7f05029b;
public static final int db_enabled_toast=0x7f050351;
/** <string name="db_store_done">SD card write complete.</string>
*/
public static final int db_store_done=0x7f05032d;

View file

@ -13,11 +13,12 @@
</style>
</head>
<body>
<h2>Crosswords 4.4.110 release</h2>
<h2>Crosswords 4.4.111 release</h2>
<p>This release fixes a couple of rare crashes added by the last
release. That release's experimental support for side-by-side
views on tablets remains the focus.</p>
<p>This release changes the display order of games in the main
screen, placing those in which it's your turn at the top and games
that have ended at the bottom. And it fixes an occasional crash in
side-by-side mode.</p>
<div id="survey">
<p>Please <a href="https://www.surveymonkey.com/s/GX3XLHR">take
@ -27,20 +28,12 @@
<h3>New with this release</h3>
<ul>
<li>Add side-by-side mode for tablets. For now, it's an option
tablet owners will be encouraged to enable. Later it'll always
be on.</li>
<li>Display at the top of the list games in which it's the local
player's turn</li>
<li>Improve board snapshots</li>
<li>Display at the bottom of the list games that have ended</li>
<li>Make alerts' content scrollable (for very small screens)</li>
<li>Move "Play sound" preference</li>
<li>Remove old ways of zooming board (since modern phones have
multi-touch screens)</li>
<li>Fix invitations creating unclosable games</li>
<li>Fix crash launching app in side-by-side mode</li>
</ul>
<p>(The full changelog

View file

@ -1661,8 +1661,10 @@ Java_org_eehouse_android_xw4_jni_XwJNI_game_1summarize
setInt( env, jsummary, "nMoves", nMoves );
XP_Bool gameOver = server_getGameIsOver( state->game.server );
setBool( env, jsummary, "gameOver", gameOver );
XP_Bool isLocal;
setInt( env, jsummary, "turn",
server_getCurrentTurn( state->game.server ) );
server_getCurrentTurn( state->game.server, &isLocal ) );
setBool( env, jsummary, "turnIsLocal", isLocal );
setInt( env, jsummary, "lastMoveTime",
server_getLastMoveTime(state->game.server) );

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_version">4.4.110</string>
<string name="app_version">4.4.111</string>
</resources>

View file

@ -2694,5 +2694,4 @@
<string name="dualpane_restart">Exiting app…</string>
<string name="after_restart">This change will not take effect until
you restart Crosswords.</string>
<string name="db_enabled_toast">Side-by-side mode ENABLED</string>
</resources>

View file

@ -2302,5 +2302,4 @@
<string name="dualpane_restart">Gnitixe ppa…</string>
<string name="after_restart">Siht egnahc lliw ton ekat tceffe litnu
uoy tratser Sdrowssorc.</string>
<string name="db_enabled_toast">Edis-yb-edis edom DELBANE</string>
</resources>

View file

@ -2302,5 +2302,4 @@
<string name="dualpane_restart">EXITING APP…</string>
<string name="after_restart">THIS CHANGE WILL NOT TAKE EFFECT UNTIL
YOU RESTART CROSSWORDS.</string>
<string name="db_enabled_toast">SIDE-BY-SIDE MODE ENABLED</string>
</resources>

View file

@ -77,7 +77,9 @@ public class BoardContainer extends ViewGroup {
if ( 0 != width || 0 != height ) {
setForPortrait( width, height );
figureBounds( 0, 0, width, height );
// Add a margin of half a percent of the lesser of width,height
int padding = (Math.min( width, height ) * 5) / 1000;
figureBounds( padding, padding, width-(padding*2), height-(padding*2) );
// Measure any toolbar first so we can take extra space for the
// board

View file

@ -67,7 +67,6 @@ public class ChatDelegate extends DelegateBase {
@Override
protected void init( Bundle savedInstanceState )
{
DbgUtils.logf( "ChatDelegate.init()" );
m_edit = (EditText)findViewById( R.id.chat_edit );
m_edit.addTextChangedListener( new TextWatcher() {
public void afterTextChanged( Editable s ) {

View file

@ -46,12 +46,13 @@ public class DBHelper extends SQLiteOpenHelper {
public static final String TABLE_NAME_CHAT = "chat";
public static final String TABLE_NAME_LOGS = "logs";
private static final String DB_NAME = "xwdb";
private static final int DB_VERSION = 27;
private static final int DB_VERSION = 28;
public static final String GAME_NAME = "GAME_NAME";
public static final String VISID = "VISID";
public static final String NUM_MOVES = "NUM_MOVES";
public static final String TURN = "TURN";
public static final String TURN_LOCAL = "TURN_LOCAL";
public static final String GIFLAGS = "GIFLAGS";
public static final String PLAYERS = "PLAYERS";
@ -126,6 +127,7 @@ public class DBHelper extends SQLiteOpenHelper {
,{ GAME_NAME, "TEXT" }
,{ NUM_MOVES, "INTEGER" }
,{ TURN, "INTEGER" }
,{ TURN_LOCAL, "INTEGER" }
,{ GIFLAGS, "INTEGER" }
,{ NUM_PLAYERS, "INTEGER" }
,{ MISSINGPLYRS,"INTEGER" }
@ -322,6 +324,10 @@ public class DBHelper extends SQLiteOpenHelper {
createChatsTable( db );
case 26:
createLogsTable( db );
case 27:
if ( !madeSumTable ) {
addSumColumn( db, TURN_LOCAL );
}
break;
default:

View file

@ -125,7 +125,7 @@ public class DBUtils {
DBHelper.NUM_MOVES, DBHelper.NUM_PLAYERS,
DBHelper.MISSINGPLYRS,
DBHelper.GAME_OVER, DBHelper.PLAYERS,
DBHelper.TURN, DBHelper.GIFLAGS,
DBHelper.TURN, DBHelper.TURN_LOCAL, DBHelper.GIFLAGS,
DBHelper.CONTYPE, DBHelper.SERVERROLE,
DBHelper.ROOMNAME, DBHelper.RELAYID,
/*DBHelper.SMSPHONE,*/ DBHelper.SEED,
@ -159,6 +159,8 @@ public class DBUtils {
summary.turn =
cursor.getInt(cursor.
getColumnIndex(DBHelper.TURN));
summary.turnIsLocal = 0 != cursor.getInt(cursor.
getColumnIndex(DBHelper.TURN_LOCAL));
summary.
setGiFlags( cursor.getInt(cursor.
getColumnIndex(DBHelper.GIFLAGS))
@ -269,6 +271,7 @@ public class DBUtils {
values.put( DBHelper.NUM_PLAYERS, summary.nPlayers );
values.put( DBHelper.MISSINGPLYRS, summary.missingPlayers );
values.put( DBHelper.TURN, summary.turn );
values.put( DBHelper.TURN_LOCAL, summary.turnIsLocal? 1 : 0 );
values.put( DBHelper.GIFLAGS, summary.giflags() );
values.put( DBHelper.PLAYERS,
summary.summarizePlayers() );
@ -1627,7 +1630,8 @@ public class DBUtils {
initDB( context );
String[] columns = { ROW_ID };
String selection = String.format( "%s=%d", DBHelper.GROUPID, groupID );
String orderBy = DBHelper.CREATE_TIME + " DESC";
String orderBy = String.format( "%s,%s DESC,%s DESC", DBHelper.GAME_OVER,
DBHelper.TURN_LOCAL, DBHelper.LASTMOVE );
synchronized( s_dbHelper ) {
SQLiteDatabase db = s_dbHelper.getReadableDatabase();
Cursor cursor = db.query( DBHelper.TABLE_NAME_SUM, columns,

View file

@ -124,11 +124,12 @@ public class DelegateBase implements DlgClickNotify,
protected void onStart()
{
if ( s_instances.containsKey(getClass()) ) {
Class clazz = getClass();
if ( s_instances.containsKey( clazz ) ) {
DbgUtils.logdf( "%s.onStart(): replacing curThis",
getClass().getSimpleName() );
clazz.getSimpleName() );
}
s_instances.put( getClass(), new WeakReference<DelegateBase>(this) );
s_instances.put( clazz, new WeakReference<DelegateBase>(this) );
}
protected void onResume()
@ -569,10 +570,16 @@ public class DelegateBase implements DlgClickNotify,
protected boolean isVisible() { return m_isVisible; }
protected boolean handleNewIntent( Intent intent ) {
DbgUtils.logf( "%s.handleNewIntent(%s): not handling",
getClass().getSimpleName(), intent.toString() );
return false; // not handled
protected boolean canHandleNewIntent( Intent intent )
{
DbgUtils.logdf( "%s.canHandleNewIntent() => false",
getClass().getSimpleName(), intent.toString() );
return false;
}
protected void handleNewIntent( Intent intent ) {
DbgUtils.logdf( "%s.handleNewIntent(%s): not handling",
getClass().getSimpleName(), intent.toString() );
}
protected void runWhenActive( Runnable proc )

View file

@ -965,9 +965,11 @@ public class DlgDelegate {
Dialog result = null;
DlgID dlgID = DlgID.values()[id];
WeakReference<DelegateBase> ref = s_pendings.get( dlgID );
DelegateBase dlgt = ref.get();
if ( null != dlgt ) {
result = dlgt.onCreateDialog( id );
if ( null != ref ) {
DelegateBase dlgt = ref.get();
if ( null != dlgt ) {
result = dlgt.onCreateDialog( id );
}
}
return result;
}

View file

@ -56,12 +56,11 @@ public class DualpaneDelegate extends DelegateBase {
}
@Override
protected boolean handleNewIntent( Intent intent )
protected void handleNewIntent( Intent intent )
{
MainActivity main = (MainActivity)m_activity;
boolean handled = main.dispatchNewIntent( intent );
DbgUtils.logf( "DualpaneDelegate.handleNewIntent() => %b", handled );
return handled;
main.dispatchNewIntent( intent );
DbgUtils.logf( "DualpaneDelegate.handleNewIntent()" );
}
@Override

View file

@ -958,7 +958,6 @@ public class GameUtils {
CommsAddrRec ret, MultiMsgSink sink,
BackMoveResult bmr, boolean[] isLocalOut )
{
DbgUtils.logf( "GameUtils.feedMessage()" );
Assert.assertTrue( DBUtils.ROWID_NOTFOUND != rowid );
boolean draw = false;
Assert.assertTrue( -1 != rowid );

View file

@ -400,14 +400,9 @@ public class GamesListDelegate extends ListDelegateBase
private List<Object> makeChildren( long groupID )
{
List<Object> alist = new ArrayList<Object>();
long[] rows = DBUtils.getGroupGames( m_activity, groupID );
List<Object> alist = new ArrayList<Object>( rows.length );
for ( long row : rows ) {
if ( false && BuildConfig.DEBUG ) {
GameUtils.BackMoveResult bmr = new GameUtils.BackMoveResult();
bmr.m_lmi = new LastMoveInfo();
GameUtils.postMoveNotification( m_activity, row, bmr, false );
}
alist.add( new GameRec( row ) );
}
// DbgUtils.logf( "GamesListDelegate.makeChildren(%d) => %d kids", groupID, alist.size() );
@ -988,17 +983,19 @@ public class GamesListDelegate extends ListDelegateBase
} // init
@Override
protected boolean handleNewIntent( Intent intent )
protected boolean canHandleNewIntent( Intent intent )
{
DbgUtils.logf( "GamesListDelegate.handleNewIntent(%s)",
intent.toString() );
return true;
}
@Override
protected void handleNewIntent( Intent intent )
{
m_launchedGames.clear();
Assert.assertNotNull( intent );
invalRelayIDs( intent.getStringArrayExtra( RELAYIDS_EXTRA ) );
reloadGame( intent.getLongExtra( ROWID_EXTRA, -1 ) );
tryStartsFromIntent( intent );
return true; // handled it
}
@Override
@ -1114,6 +1111,11 @@ public class GamesListDelegate extends ListDelegateBase
mkListAdapter();
} else {
reloadGame( rowid );
if ( m_adapter.inExpandedGroup( rowid ) ) {
long groupID = DBUtils.getGroupForGame( m_activity, rowid );
m_adapter.setExpanded( groupID, false );
m_adapter.setExpanded( groupID, true );
}
}
break;
case GAME_CREATED:
@ -1660,6 +1662,7 @@ public class GamesListDelegate extends ListDelegateBase
final boolean success )
{
runWhenActive( new Runnable() {
@Override
public void run() {
boolean madeGame = false;
if ( success ) {

View file

@ -1,6 +1,6 @@
/* -*- compile-command: "find-and-ant.sh debug install"; -*- */
/*
* Copyright 2009 - 2014 by Eric House (xwords@eehouse.org). All
* Copyright 2009 - 2016 by Eric House (xwords@eehouse.org). All
* rights reserved.
*
* This program is free software; you can redistribute it and/or
@ -37,6 +37,7 @@ import android.widget.LinearLayout;
import junit.framework.Assert;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
@ -54,7 +55,8 @@ public class MainActivity extends XWActivity
private int m_nextID = 0x00FFFFFF;
private Boolean m_isPortrait;
private boolean m_safeToCommit;
private Runnable m_runWhenSafe;
private ArrayList<Runnable> m_runWhenSafe = new ArrayList<Runnable>();
private Intent m_newIntent; // work in progress...
// for tracking launchForResult callback recipients
private Map<RequestCode, WeakReference<DelegateBase>> m_pendingCodes
@ -64,9 +66,6 @@ public class MainActivity extends XWActivity
protected void onCreate( Bundle savedInstanceState )
{
m_dpEnabled = XWPrefs.dualpaneEnabled( this );
if ( m_dpEnabled ) {
Utils.showToast( this, R.string.db_enabled_toast );
}
m_dlgt = m_dpEnabled ? new DualpaneDelegate( this, savedInstanceState )
: new GamesListDelegate( this, savedInstanceState );
@ -141,52 +140,89 @@ public class MainActivity extends XWActivity
super.onConfigurationChanged( newConfig );
}
/**
* Run down the list of fragments until one handles the intent. If no
* visible one does, pop hidden ones into view until one of them
* does. Yes, this will take us back to GamesList being visible even if
* nothing handles the intent, but at least now all are handled by
* GamesList anyway.
/* Sometimes I'm getting crashes because views don't have fragments
* associated yet. I suspect that's because adding them's been postponed
* via the m_runWhenSafe mechanism. So: postpone handling intents too.
*
* This postponing thing won't scale, and makes me suspect there's
* something I'm doing wrong w.r.t. fragments. Should be revisited. In
* this particular case there might be a better way to get to the Delegate
* on which I need to call handleNewIntent().
*/
protected boolean dispatchNewIntent( Intent intent )
protected boolean dispatchNewIntent( final Intent intent )
{
boolean handled;
if ( m_safeToCommit ) {
handled = dispatchNewIntentImpl( intent );
} else {
m_runWhenSafe.add( new Runnable() {
@Override
public void run() {
dispatchNewIntentImpl( intent );
}
} );
if ( BuildConfig.DEBUG ) {
DbgUtils.showf( this, "Putting off handling intent; %d waiting",
m_runWhenSafe.size() );
}
handled = true;
}
return handled;
}
private void popIntoView( XWFragment newTopFrag )
{
FragmentManager fm = getSupportFragmentManager();
for ( ; ; ) {
int top = m_root.getChildCount() - 1;
if ( top < 0 ) {
break;
}
View child = m_root.getChildAt( top );
XWFragment frag = (XWFragment)fm.findFragmentById( child.getId() );
if ( frag == newTopFrag ) {
break;
}
String name = frag.getClass().getSimpleName();
DbgUtils.logdf( "MainActivity.popIntoView(): popping %d: %s", top, name );
fm.popBackStackImmediate();
DbgUtils.logdf( "MainActivity.popIntoView(): DONE popping %s",
name );
}
}
/**
* Run down the list of fragments until one can handle the intent. If
* necessary, pop fragments above it until it comes into view. Then send
* it the event.
*/
private boolean dispatchNewIntentImpl( Intent intent )
{
boolean handled = false;
FragmentManager fm = getSupportFragmentManager();
// First try non-left-most fragments, if any. Once we've eliminated
// them we can just iterate on the leftmost fragment.
int nNonLeft = m_maxPanes - 1;
// include paged-to-left invisible views
int viewCount = m_root.getChildCount();
for ( int ii = nNonLeft; !handled && 0 < ii; --ii ) {
View child = m_root.getChildAt( viewCount - ii );
Fragment frag = fm.findFragmentById( child.getId() );
handled = ((XWFragment)frag).getDelegate().handleNewIntent( intent );
}
while ( !handled ) {
// Now iterate on the leftmost, popping if necessary to page new
// ones into place
int childCount = m_root.getChildCount();
int hiddenCount = Math.max( 0, childCount - m_maxPanes );
for ( int ii = hiddenCount; ii >= 0; --ii ) {
View child = m_root.getChildAt( ii );
Fragment frag = fm.findFragmentById( child.getId() );
// DbgUtils.logf( "left-most case (child %d): %s", hiddenCount,
// frag.getClass().getSimpleName() );
handled = ((XWFragment)frag).getDelegate()
.handleNewIntent( intent );
for ( int ii = m_root.getChildCount() - 1; !handled && ii >= 0; --ii ) {
View child = m_root.getChildAt( ii );
XWFragment frag = (XWFragment)fm.findFragmentById( child.getId() );
if ( null != frag ) {
handled = frag.getDelegate().canHandleNewIntent( intent );
if ( handled ) {
break;
} else if ( ii > 0 ) {
DbgUtils.logf( "popping %s",
frag.getClass().getSimpleName() );
fm.popBackStackImmediate(); // callback removes view
popIntoView( frag );
frag.getDelegate().handleNewIntent( intent );
}
} else {
DbgUtils.logdf( "no fragment for child %s indx %d",
child.getClass().getSimpleName(), ii );
}
}
if ( BuildConfig.DEBUG && !handled ) {
// DbgUtils.showf( this, "dropping intent %s", intent.toString() );
DbgUtils.logdf( "dropping intent %s", intent.toString() );
// DbgUtils.printStack();
// setIntent( intent ); -- look at handling this in onPostResume()?
m_newIntent = intent;
}
return handled;
}
@ -335,9 +371,12 @@ public class MainActivity extends XWActivity
private XWFragment getTopFragment()
{
XWFragment frag = null;
View child = m_root.getChildAt( m_root.getChildCount() - 1 );
XWFragment frag = (XWFragment)getSupportFragmentManager()
.findFragmentById( child.getId() );
if ( null != child ) {
frag = (XWFragment)getSupportFragmentManager()
.findFragmentById( child.getId() );
}
return frag;
}
@ -438,15 +477,15 @@ public class MainActivity extends XWActivity
if ( m_safeToCommit ) {
safeAddFragment( fragment, parentName );
} else {
Assert.assertNull( m_runWhenSafe );
m_runWhenSafe = new Runnable() {
m_runWhenSafe.add( new Runnable() {
@Override
public void run() {
safeAddFragment( fragment, parentName );
}
};
} );
if ( BuildConfig.DEBUG ) {
DbgUtils.showf( this, "Putting off fragment construction" );
DbgUtils.showf( this, "Putting off fragment construction; %d waiting",
m_runWhenSafe.size() );
}
}
}
@ -517,9 +556,9 @@ public class MainActivity extends XWActivity
private void setSafeToRun()
{
m_safeToCommit = true;
if ( null != m_runWhenSafe ) {
m_runWhenSafe.run();
m_runWhenSafe = null;
for ( Runnable proc : m_runWhenSafe ) {
proc.run();
}
m_runWhenSafe.clear();
}
}

View file

@ -163,7 +163,6 @@ public class Toolbar implements BoardContainer.SizeChangeListener {
private void doShowHide()
{
boolean isPortrait = BoardContainer.getIsPortrait();
DbgUtils.logdf( "Toolbar.doShowHide(): isPortrait: %b", isPortrait );
if ( null == m_layout ) {
m_layout = (LinearLayout)LocUtils.inflate( m_activity, R.layout.toolbar );

View file

@ -1,6 +1,7 @@
/* -*- compile-command: "find-and-ant.sh debug install"; -*- */
/*
* Copyright 2014 by Eric House (xwords@eehouse.org). All rights reserved.
* Copyright 2014-2016 by Eric House (xwords@eehouse.org). All rights
* reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@ -85,6 +86,16 @@ public class XWActivity extends FragmentActivity implements Delegator {
m_dlgt.onResume();
}
@Override
protected void onPostResume()
{
if ( XWApp.LOG_LIFECYLE ) {
DbgUtils.logf( "%s.onPostResume(this=%H)",
getClass().getSimpleName(), this );
}
super.onPostResume();
}
@Override
protected void onStart()
{

View file

@ -50,9 +50,10 @@ public class GameSummary {
public static final int MSG_FLAGS_GAMEOVER = 4;
public static final int MSG_FLAGS_ALL = 7;
public int lastMoveTime;
public int lastMoveTime; // set by jni's server.c on move receipt
public int nMoves;
public int turn;
public boolean turnIsLocal;
public int nPlayers;
public int missingPlayers;
public int[] scores;

View file

@ -8,12 +8,12 @@ declare -A PACKAGES=()
declare -A SERIALS=()
INDEX=''
DRYRUN=''
ALL=''
ALL_DEVS=''
AAPT=''
usage() {
[ $# -gt 0 ] && echo "ERROR: $1"
echo "usage: $0 [--dry-run] [--help] "
echo "usage: $0 [--dry-run] [--help] [--all-devs]"
echo " [--apk <path/to/apk>] # default is to use package names of all known apks"
echo " [--pkg-name tld.whatever.more] # explicit package name"
echo " [--serial <serial>] # default is to use all attached devices"
@ -65,6 +65,9 @@ while [ $# -ge 1 ]; do
--dry-run)
DRYRUN=1
;;
--all-devs)
ALL_DEVS=1
;;
--help)
usage
;;
@ -88,6 +91,12 @@ if [ 0 = "${#SERIALS[*]}" ]; then
for DEV in $(adb devices | grep '\sdevice$' | awk '{print $1}'); do
SERIALS[$DEV]=1
done
if [ 0 = "${#SERIALS[*]}" ]; then
usage "no devices found"
elif [ 1 -lt "${#SERIALS[*]}" -a -z "$ALL_DEVS" ]; then
usage "More than one device found. Be specific, or use --all-devs"
fi
fi
for PACKAGE in "${!PACKAGES[@]}"; do

View file

@ -792,7 +792,7 @@ board_canShuffle( const BoardCtxt* board )
XP_Bool
board_canHideRack( const BoardCtxt* board )
{
XP_Bool result = 0 <= server_getCurrentTurn( board->server )
XP_Bool result = 0 <= server_getCurrentTurn( board->server, NULL )
&& (board->boardObscuresTray || !board->gameOver);
return result;
}
@ -988,7 +988,7 @@ warnBadWords( const XP_UCHAR* word, XP_Bool isLegal,
if ( !isLegal ) {
BadWordInfo bwi = {0};
BoardCtxt* board = (BoardCtxt*)closure;
XP_S16 turn = server_getCurrentTurn( board->server );
XP_S16 turn = server_getCurrentTurn( board->server, NULL );
bwi.nWords = 1;
bwi.words[0] = word;
@ -1019,7 +1019,7 @@ XP_Bool
board_commitTurn( BoardCtxt* board )
{
XP_Bool result = XP_FALSE;
const XP_S16 turn = server_getCurrentTurn( board->server );
const XP_S16 turn = server_getCurrentTurn( board->server, NULL );
PerTurnInfo* pti = board->pti + turn;
if ( board->gameOver || turn < 0 ) {
@ -1110,15 +1110,15 @@ static void
selectPlayerImpl( BoardCtxt* board, XP_U16 newPlayer, XP_Bool reveal,
XP_Bool canPeek )
{
XP_S16 curTurn = server_getCurrentTurn(board->server);
XP_Bool isLocal;
XP_S16 curTurn = server_getCurrentTurn( board->server, &isLocal );
if ( !board->gameOver && curTurn < 0 ) {
/* game not started yet; do nothing */
} else if ( board->selPlayer == newPlayer ) {
if ( reveal ) {
checkRevealTray( board );
}
} else if ( canPeek || ((newPlayer == curTurn)
&& LP_IS_LOCAL( &board->gi->players[newPlayer]))) {
} else if ( canPeek || ((newPlayer == curTurn) && isLocal)) {
PerTurnInfo* newInfo = &board->pti[newPlayer];
XP_U16 oldPlayer = board->selPlayer;
model_foreachPendingCell( board->model, newPlayer,
@ -1318,7 +1318,7 @@ timerFiredForTimer( BoardCtxt* board )
{
board->timerPending = XP_FALSE;
if ( !board->gameOver ) {
XP_S16 turn = server_getCurrentTurn( board->server );
XP_S16 turn = server_getCurrentTurn( board->server, NULL );
if ( turn >= 0 ) {
++board->gi->players[turn].secondsUsed;
@ -1365,7 +1365,8 @@ board_pushTimerSave( BoardCtxt* board )
if ( board->timerSaveCount++ == 0 ) {
board->timerStoppedTime = util_getCurSeconds( board->util );
#ifdef DEBUG
board->timerStoppedTurn = server_getCurrentTurn( board->server );
board->timerStoppedTurn = server_getCurrentTurn( board->server,
NULL );
#endif
}
}
@ -1380,7 +1381,7 @@ board_popTimerSave( BoardCtxt* board )
between calls to board_pushTimerSave and this call, as can happen on
franklin. So that's not an error. */
if ( board->timerSaveCount > 0 ) {
XP_S16 turn = server_getCurrentTurn( board->server );
XP_S16 turn = server_getCurrentTurn( board->server, NULL );
XP_ASSERT( board->timerStoppedTurn == turn );
@ -1769,7 +1770,7 @@ chooseBestSelPlayer( BoardCtxt* board )
return board->selPlayer;
} else {
XP_S16 curTurn = server_getCurrentTurn( server );
XP_S16 curTurn = server_getCurrentTurn( server, NULL );
if ( curTurn >= 0 ) {
XP_U16 nPlayers = board->gi->nPlayers;
@ -2001,7 +2002,7 @@ static XP_Bool
preflight( BoardCtxt* board, XP_Bool reveal )
{
return !board->gameOver
&& server_getCurrentTurn(board->server) >= 0
&& server_getCurrentTurn(board->server, NULL) >= 0
&& ( !reveal || checkRevealTray( board ) )
&& !TRADE_IN_PROGRESS(board);
} /* preflight */
@ -2581,7 +2582,7 @@ askRevealTray( BoardCtxt* board )
if ( board->gameOver ) {
revealed = XP_TRUE;
} else if ( server_getCurrentTurn( board->server ) < 0 ) {
} else if ( server_getCurrentTurn( board->server, NULL ) < 0 ) {
revealed = XP_FALSE;
#ifndef XWFEATURE_STANDALONE_ONLY
} else if ( !lp->isLocal ) {

View file

@ -240,7 +240,7 @@ struct BoardCtxt {
# define valHintMiniWindowActive( board ) \
((XP_Bool)((board)->miniWindowStuff[MINIWINDOW_VALHINT].text != NULL))
#endif
#define MY_TURN(b) ((b)->selPlayer == server_getCurrentTurn( (b)->server ))
#define MY_TURN(b) ((b)->selPlayer == server_getCurrentTurn( (b)->server, NULL ))
#define TRADE_IN_PROGRESS(b) ((b)->selInfo->tradeInProgress==XP_TRUE)
/* tray-related functions */

View file

@ -176,7 +176,7 @@ drawScoreBoard( BoardCtxt* board )
XP_ASSERT( nPlayers <= MAX_NUM_PLAYERS );
if ( nPlayers > 0 ) {
ModelCtxt* model = board->model;
XP_S16 curTurn = server_getCurrentTurn( board->server );
XP_S16 curTurn = server_getCurrentTurn( board->server, NULL );
XP_U16 selPlayer = board->selPlayer;
XP_S16 nTilesInPool = server_countTilesInPool( board->server );
XP_Rect scoreRect = board->scoreBdBounds;

View file

@ -2435,9 +2435,13 @@ server_commitTrade( ServerCtxt* server, const TrayTileSet* oldTiles )
} /* server_commitTrade */
XP_S16
server_getCurrentTurn( ServerCtxt* server )
server_getCurrentTurn( ServerCtxt* server, XP_Bool* isLocal )
{
return server->nv.currentTurn;
XP_S16 turn = server->nv.currentTurn;
if ( NULL != isLocal && turn >= 0 ) {
*isLocal = server->vol.gi->players[turn].isLocal;
}
return turn;
} /* server_getCurrentTurn */
XP_Bool

View file

@ -94,7 +94,7 @@ XP_U16 server_secondsUsedBy( ServerCtxt* server, XP_U16 playerNum );
XP_Bool server_handleUndo( ServerCtxt* server, XP_U16 limit );
/* signed because negative number means nobody's turn yet */
XP_S16 server_getCurrentTurn( ServerCtxt* server );
XP_S16 server_getCurrentTurn( ServerCtxt* server, XP_Bool* isLocal );
XP_Bool server_getGameIsOver( ServerCtxt* server );
/* return bitvector marking players still not arrived in networked game */
XP_U16 server_getMissingPlayers( const ServerCtxt* server );

View file

@ -339,7 +339,7 @@ drawPendingScore( BoardCtxt* board, XP_S16 turnScore, XP_Bool hasCursor )
/* Draw the pending score down in the last tray's rect */
if ( countTilesToShow( board ) < MAX_TRAY_TILES ) {
XP_U16 selPlayer = board->selPlayer;
XP_S16 curTurn = server_getCurrentTurn( board->server );
XP_S16 curTurn = server_getCurrentTurn( board->server, NULL );
XP_Rect lastTileR;
figureTrayTileRect( board, MAX_TRAY_TILES-1, &lastTileR );

View file

@ -1,6 +1,6 @@
/* -*- compile-command: "make MEMDEBUG=TRUE -j3"; -*- */
/* -*- compile-command: "make MEMDEBUG=TRUE -j5"; -*- */
/*
* Copyright 2000-2013 by Eric House (xwords@eehouse.org). All rights
* Copyright 2000 - 2016 by Eric House (xwords@eehouse.org). All rights
* reserved.
*
* This program is free software; you can redistribute it and/or
@ -54,6 +54,7 @@ openGamesDB( const char* dbName )
",connvia VARCHAR(32)"
",ended INT(1)"
",turn INT(2)"
",local INT(1)"
",nmoves INT"
",seed INT"
",gameid INT"
@ -197,7 +198,8 @@ summarize( CommonGlobals* cGlobals )
{
XP_S16 nMoves = model_getNMoves( cGlobals->game.model );
XP_Bool gameOver = server_getGameIsOver( cGlobals->game.server );
XP_S16 turn = server_getCurrentTurn( cGlobals->game.server );
XP_Bool isLocal;
XP_S16 turn = server_getCurrentTurn( cGlobals->game.server, &isLocal );
XP_U32 lastMoveTime = server_getLastMoveTime( cGlobals->game.server );
XP_U16 seed = 0;
XP_S16 nMissing = 0;
@ -243,12 +245,13 @@ summarize( CommonGlobals* cGlobals )
}
const char* fmt = "UPDATE games "
" SET room='%s', ended=%d, turn=%d, ntotal=%d, nmissing=%d, nmoves=%d, seed=%d, "
" gameid=%d, connvia='%s', lastMoveTime=%d"
" SET room='%s', ended=%d, turn=%d, local=%d, ntotal=%d, nmissing=%d, "
" nmoves=%d, seed=%d, gameid=%d, connvia='%s', lastMoveTime=%d"
" WHERE rowid=%lld";
XP_UCHAR buf[256];
snprintf( buf, sizeof(buf), fmt, room, gameOver?1:0, turn, nTotal, nMissing,
nMoves, seed, gameID, connvia, lastMoveTime, cGlobals->selRow );
snprintf( buf, sizeof(buf), fmt, room, gameOver?1:0, turn, isLocal?1:0,
nTotal, nMissing, nMoves, seed, gameID, connvia, lastMoveTime,
cGlobals->selRow );
XP_LOGF( "query: %s", buf );
sqlite3_stmt* stmt = NULL;
int result = sqlite3_prepare_v2( cGlobals->pDb, buf, -1, &stmt, NULL );
@ -301,7 +304,7 @@ XP_Bool
getGameInfo( sqlite3* pDb, sqlite3_int64 rowid, GameInfo* gib )
{
XP_Bool success = XP_FALSE;
const char* fmt = "SELECT room, ended, turn, nmoves, ntotal, nmissing, "
const char* fmt = "SELECT room, ended, turn, local, nmoves, ntotal, nmissing, "
"seed, connvia, gameid, lastMoveTime, snap "
"FROM games WHERE rowid = %lld";
XP_UCHAR query[256];
@ -316,20 +319,21 @@ getGameInfo( sqlite3* pDb, sqlite3_int64 rowid, GameInfo* gib )
getColumnText( ppStmt, 0, gib->room, sizeof(gib->room) );
gib->gameOver = 1 == sqlite3_column_int( ppStmt, 1 );
gib->turn = sqlite3_column_int( ppStmt, 2 );
gib->nMoves = sqlite3_column_int( ppStmt, 3 );
gib->nTotal = sqlite3_column_int( ppStmt, 4 );
gib->nMissing = sqlite3_column_int( ppStmt, 5 );
gib->seed = sqlite3_column_int( ppStmt, 6 );
getColumnText( ppStmt, 7, gib->conn, sizeof(gib->conn) );
gib->gameID = sqlite3_column_int( ppStmt, 8 );
gib->lastMoveTime = sqlite3_column_int( ppStmt, 9 );
gib->turnLocal = 1 == sqlite3_column_int( ppStmt, 3 );
gib->nMoves = sqlite3_column_int( ppStmt, 4 );
gib->nTotal = sqlite3_column_int( ppStmt, 5 );
gib->nMissing = sqlite3_column_int( ppStmt, 6 );
gib->seed = sqlite3_column_int( ppStmt, 7 );
getColumnText( ppStmt, 8, gib->conn, sizeof(gib->conn) );
gib->gameID = sqlite3_column_int( ppStmt, 9 );
gib->lastMoveTime = sqlite3_column_int( ppStmt, 10 );
snprintf( gib->name, sizeof(gib->name), "Game %lld", rowid );
/* Load the snapshot */
GdkPixbuf* snap = NULL;
const XP_U8* ptr = sqlite3_column_blob( ppStmt, 10 );
const XP_U8* ptr = sqlite3_column_blob( ppStmt, 11 );
if ( !!ptr ) {
int size = sqlite3_column_bytes( ppStmt, 10 );
int size = sqlite3_column_bytes( ppStmt, 11 );
/* Skip the version that's written in */
ptr += sizeof(XP_U16); size -= sizeof(XP_U16);
GInputStream* istr = g_memory_input_stream_new_from_data( ptr, size, NULL );

View file

@ -1,6 +1,6 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; compile-command: "make MEMDEBUG=TRUE -j3"; -*- */
/* -*- compile-command: "make MEMDEBUG=TRUE -j5"; -*- */
/*
* Copyright 2000-2012 by Eric House (xwords@eehouse.org). All rights
* Copyright 2000 - 2016 by Eric House (xwords@eehouse.org). All rights
* reserved.
*
* This program is free software; you can redistribute it and/or
@ -35,6 +35,7 @@ typedef struct _GameInfo {
XP_U32 gameID;
XP_S16 nMoves;
XP_Bool gameOver;
XP_Bool turnLocal;
XP_S16 turn;
XP_U16 nTotal;
XP_S16 nMissing;

View file

@ -2695,7 +2695,7 @@ initGlobals( GtkGameGlobals* globals, LaunchParams* params, CurGameInfo* gi )
g_signal_connect( globals->adjustment, "value_changed",
G_CALLBACK(scroll_value_changed), globals );
gtk_widget_show( vscrollbar );
gtk_box_pack_start( GTK_BOX(hbox), vscrollbar, TRUE, TRUE, 0 );
gtk_box_pack_start( GTK_BOX(hbox), vscrollbar, FALSE, FALSE, 0 );
gtk_box_pack_start( GTK_BOX (hbox),
makeVerticalBar( globals, window ),

View file

@ -1,6 +1,6 @@
/* -*- compile-command: "make MEMDEBUG=TRUE -j3"; -*- */
/* -*- compile-command: "make MEMDEBUG=TRUE -j5"; -*- */
/*
* Copyright 2000-2013 by Eric House (xwords@eehouse.org). All rights
* Copyright 2000 - 2016 by Eric House (xwords@eehouse.org). All rights
* reserved.
*
* This program is free software; you can redistribute it and/or
@ -76,8 +76,8 @@ findOpenGame( const GtkAppGlobals* apg, sqlite3_int64 rowid )
}
enum { ROW_ITEM, ROW_THUMB, NAME_ITEM, ROOM_ITEM, GAMEID_ITEM, SEED_ITEM,
CONN_ITEM, OVER_ITEM, TURN_ITEM, NMOVES_ITEM, NTOTAL_ITEM, MISSING_ITEM,
LASTTURN_ITEM, N_ITEMS };
CONN_ITEM, OVER_ITEM, TURN_ITEM, LOCAL_ITEM, NMOVES_ITEM, NTOTAL_ITEM,
MISSING_ITEM, LASTTURN_ITEM, N_ITEMS };
static void
foreachProc( GtkTreeModel* model, GtkTreePath* XP_UNUSED(path),
@ -169,6 +169,7 @@ init_games_list( GtkAppGlobals* apg )
addTextColumn( list, "Conn. via", CONN_ITEM );
addTextColumn( list, "Ended", OVER_ITEM );
addTextColumn( list, "Turn", TURN_ITEM );
addTextColumn( list, "Local", LOCAL_ITEM );
addTextColumn( list, "NMoves", NMOVES_ITEM );
addTextColumn( list, "NTotal", NTOTAL_ITEM );
addTextColumn( list, "NMissing", MISSING_ITEM );
@ -184,10 +185,11 @@ init_games_list( GtkAppGlobals* apg )
G_TYPE_STRING, /* CONN_ITEM */
G_TYPE_BOOLEAN, /* OVER_ITEM */
G_TYPE_INT, /* TURN_ITEM */
G_TYPE_STRING, /* LOCAL_ITEM */
G_TYPE_INT, /* NMOVES_ITEM */
G_TYPE_INT, /* NTOTAL_ITEM */
G_TYPE_INT, /* MISSING_ITEM */
G_TYPE_INT /* LASTTURN_ITEM */
G_TYPE_STRING /* LASTTURN_ITEM */
);
gtk_tree_view_set_model( GTK_TREE_VIEW(list), GTK_TREE_MODEL(store) );
g_object_unref( store );
@ -224,6 +226,11 @@ add_to_list( GtkWidget* list, sqlite3_int64 rowid, XP_Bool isNew,
}
}
}
gchar* localString = 0 <= gib->turn ? gib->turnLocal ? "YES"
: "NO" : "";
GTimeVal timeval = { tv_sec: gib->lastMoveTime, tv_usec: 0 };
gchar* timeStr = g_time_val_to_iso8601( &timeval );
gtk_list_store_set( store, &iter,
ROW_ITEM, rowid,
ROW_THUMB, gib->snap,
@ -232,13 +239,15 @@ add_to_list( GtkWidget* list, sqlite3_int64 rowid, XP_Bool isNew,
GAMEID_ITEM, gib->gameID,
SEED_ITEM, gib->seed,
CONN_ITEM, gib->conn,
OVER_ITEM, gib->gameOver,
TURN_ITEM, gib->turn,
OVER_ITEM, gib->gameOver,
LOCAL_ITEM, localString,
NMOVES_ITEM, gib->nMoves,
NTOTAL_ITEM, gib->nTotal,
MISSING_ITEM, gib->nMissing,
LASTTURN_ITEM, gib->lastMoveTime,
LASTTURN_ITEM, timeStr,
-1 );
g_free( timeStr );
XP_LOGF( "DONE adding" );
}
@ -483,14 +492,18 @@ setWindowTitle( GtkAppGlobals* apg )
static void
trySetWinConfig( GtkAppGlobals* apg )
{
int xx = 20; /* defaults */
int yy = 20;
int width = 600;
int height = 400;
gchar buf[64];
if ( db_fetch( apg->params->pDb, KEY_WIN_LOC, buf, sizeof(buf)) ) {
int xx, yy, width, height;
sscanf( buf, "%d:%d:%d:%d", &xx, &yy, &width, &height );
gtk_window_resize( GTK_WINDOW(apg->window), width, height );
gtk_window_move (GTK_WINDOW(apg->window), xx, yy );
}
gtk_window_resize( GTK_WINDOW(apg->window), width, height );
gtk_window_move (GTK_WINDOW(apg->window), xx, yy );
}
static void