diff --git a/README.md b/README.md
new file mode 100644
index 000000000..990995fd9
--- /dev/null
+++ b/README.md
@@ -0,0 +1,4 @@
+# xwords
+Clone of xwords repo from Sourceforge -- an open source implementation of rules of Scrabble(tm) for handhelds
+
+Just adding now. I'll edit after pulling. :-)
diff --git a/xwords4/android/app/build.gradle b/xwords4/android/app/build.gradle
index 1bd74364b..b418cec40 100644
--- a/xwords4/android/app/build.gradle
+++ b/xwords4/android/app/build.gradle
@@ -1,6 +1,6 @@
def INITIAL_CLIENT_VERS = 9
-def VERSION_CODE_BASE = 156
-def VERSION_NAME = '4.4.160'
+def VERSION_CODE_BASE = 157
+def VERSION_NAME = '4.4.161'
def FABRIC_API_KEY = System.getenv("FABRIC_API_KEY")
def BUILD_INFO_NAME = "build-info.txt"
@@ -36,10 +36,8 @@ if ( FABRIC_API_KEY && hasProperty('useCrashlytics') ) { // rm-for-fdroid
} // rm-for-fdroid
repositories {
google()
- maven { url 'https://maven.fabric.io/public' } // rm-for-fdroid
- maven {
- url "https://repo.eclipse.org/content/repositories/paho-releases/"
- }
+ maven { url 'https://maven.fabric.io/public' } // rm-for-fdroid
+ maven { url "https://repo.eclipse.org/content/repositories/paho-releases/" } // rm-for-fdroid
}
android {
@@ -122,6 +120,7 @@ android {
resValue "string", "nfc_aid", "$NFC_AID_XW4"
externalNativeBuild.ndkBuild.cFlags += ['-DVARIANT_xw4fdroid']
externalNativeBuild.ndkBuild.arguments += ['XW_BT_UUID=' + XW_UUID]
+ buildConfigField "boolean", "OFFER_MQTT", "false"
}
xw4d {
@@ -337,7 +336,7 @@ dependencies {
implementation 'com.github.eehouse:nbsproxy:v0.2.2'
- implementation "org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.1.+"
+ implementation "org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.1.+" // rm-for-fdroid
}
task mkImages(type: Exec) {
diff --git a/xwords4/android/app/src/main/assets/changes.html b/xwords4/android/app/src/main/assets/changes.html
index 497f840f8..f8726bef1 100644
--- a/xwords4/android/app/src/main/assets/changes.html
+++ b/xwords4/android/app/src/main/assets/changes.html
@@ -13,10 +13,10 @@
- CrossWords 4.4.160 release
+ CrossWords 4.4.161 release
- This release improves play via the internet by adding a second
- (and redundant) type of server.
+ This minor release removes an under-construction image I added
+ recently. It made friends think they were being hacked.
Please take
@@ -26,17 +26,17 @@
New with this release
- - Improve internet/relay play by adding a second server type (MQTT)
- - Add "under construction" icon for when thumbnail isn't available
- - Enable new "block phonies" option that won't let you use a word not in the wordlist
- - Add new Japanese translations (via Weblate)
+ - Remove under-construction image that flashed by as a thumbnail was loading
+ - Make color of line separating board tiles configurable
+ - Change how dialogs are handled internally. You should NOT notice!
(The full changelog
is here.)
- Next up
+ Coming soon
+ - Add filtering to the wordlist browser
- Improve move-via-NFC
- Support duplicate-style play (popular in France)
- Improve play-by-data-sms workaround
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/BTInviteDelegate.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/BTInviteDelegate.java
index 868486775..4367ab5b3 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/BTInviteDelegate.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/BTInviteDelegate.java
@@ -27,6 +27,7 @@ import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
+import android.text.TextUtils;
import android.text.format.DateUtils;
import android.view.View;
import android.widget.Button;
@@ -74,7 +75,7 @@ public class BTInviteDelegate extends InviteDelegate {
pairs = new ArrayList<>();
} else {
for ( TwoStringPair pair : pairs ) {
- alreadyHave = pair.str2.equals(devName);
+ alreadyHave = TextUtils.equals(pair.str2, devName);
if ( alreadyHave ) {
break;
}
@@ -96,7 +97,7 @@ public class BTInviteDelegate extends InviteDelegate {
for ( Iterator iter = pairs.iterator();
iter.hasNext(); ) {
TwoStringPair pair = iter.next();
- if ( pair.str2.equals( dev ) ) {
+ if ( TextUtils.equals( pair.str2, dev ) ) {
iter.remove();
break;
}
@@ -104,6 +105,18 @@ public class BTInviteDelegate extends InviteDelegate {
}
}
+ private void removeNulls()
+ {
+ for ( Iterator iter = pairs.iterator();
+ iter.hasNext(); ) {
+ TwoStringPair pair = iter.next();
+ if ( TextUtils.isEmpty( pair.str2 ) ) {
+ Log.d( TAG, "removeNulls(): removing!!" );
+ iter.remove();
+ }
+ }
+ }
+
boolean empty() { return pairs == null || pairs.size() == 0; }
private void sort()
@@ -315,6 +328,7 @@ public class BTInviteDelegate extends InviteDelegate {
if ( null == sPersisted ) {
try {
sPersisted = (Persisted)DBUtils.getSerializableFor( context, KEY_PERSIST );
+ sPersisted.removeNulls(); // clean up earlier mistakes
} catch ( Exception ex ) {} // NPE, de-serialization problems, etc.
if ( null == sPersisted ) {
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/BoardCanvas.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/BoardCanvas.java
index fa8a5d67d..c8a23f33c 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/BoardCanvas.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/BoardCanvas.java
@@ -49,7 +49,6 @@ public class BoardCanvas extends Canvas implements DrawCtx {
private static final String TAG = BoardCanvas.class.getSimpleName();
private static final int BLACK = 0xFF000000;
private static final int WHITE = 0xFFFFFFFF;
- private static final int FRAME_GREY = 0xFF101010;
private static final int SCORE_HT_DROP = 2;
private static final boolean DEBUG_DRAWFRAMES = false;
private static final int NOT_TURN_ALPHA = 0x3FFFFFFF;
@@ -442,7 +441,15 @@ public class BoardCanvas extends Canvas implements DrawCtx {
}
// frame the cell
- m_strokePaint.setColor( adjustColor(FRAME_GREY) );
+ int frameColor = m_otherColors[CommonPrefs.COLOR_CELLLINE];
+ m_strokePaint.setColor( adjustColor(frameColor) );
+ if ( false ) {
+ // PENDING: fetch/calculate this a lot less frequently!!
+ int linePct = XWPrefs.getPrefsInt( m_activity, R.string.key_board_line_pct, 1 );
+ linePct = Math.min( 25, linePct );
+ int width = Math.max(1, (rect.width() * linePct) / 100);
+ m_strokePaint.setStrokeWidth( width );
+ }
drawRect( rect, m_strokePaint );
drawCrosshairs( rect, flags );
@@ -559,7 +566,8 @@ public class BoardCanvas extends Canvas implements DrawCtx {
// the board (without scrolling), the right-most cells
// don't draw their right borders due to clipping, so draw
// for them.
- m_strokePaint.setColor( adjustColor(FRAME_GREY) );
+ int frameColor = m_otherColors[CommonPrefs.COLOR_CELLLINE];
+ m_strokePaint.setColor( adjustColor(frameColor) );
int xx = rect.left + rect.width() - 1;
drawLine( xx, rect.top, xx, rect.top + rect.height(),
m_strokePaint );
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 b6a5310e0..c0e77630b 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
@@ -315,8 +315,9 @@ public class BoardDelegate extends DelegateBase
int title = (Integer)params[0];
String msg = (String)params[1];
ab.setMessage( msg );
- Assert.assertTrue( 0 != title );
- ab.setTitle( title );
+ if ( 0 != title ) {
+ ab.setTitle( title );
+ }
ab.setPositiveButton( android.R.string.ok, null );
if ( DlgID.DLG_SCORES == dlgID ) {
if ( null != m_mySIS.words && m_mySIS.words.length > 0 ) {
@@ -2488,7 +2489,7 @@ public class BoardDelegate extends DelegateBase
boolean banned = Perm.SEND_SMS.isBanned(m_activity);
int explID = banned
? R.string.banned_nbs_perms : R.string.missing_sms_perms;
- DlgDelegate.ConfirmThenBuilder builder =
+ DlgDelegate.Builder builder =
makeConfirmThenBuilder( explID, Action.DROP_SMS_ACTION );
if ( banned ) {
builder.setActionPair( Action.PERMS_BANNED_INFO,
@@ -2635,7 +2636,6 @@ public class BoardDelegate extends DelegateBase
switch ( dlgID ) {
case DLG_OKONLY:
case DLG_SCORES:
- dlgTitle = R.string.info_title;
break;
case DLG_USEDICT:
case DLG_GETDICT:
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/ConfirmThenAlert.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/ConfirmThenAlert.java
deleted file mode 100644
index c875d674a..000000000
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/ConfirmThenAlert.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/* -*- compile-command: "find-and-gradle.sh inXw4dDebug"; -*- */
-/*
- * Copyright 2017 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
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-package org.eehouse.android.xw4;
-
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.content.Context;
-import android.content.DialogInterface.OnClickListener;
-import android.os.Bundle;
-
-import org.eehouse.android.xw4.DlgDelegate.ActionPair;
-import org.eehouse.android.xw4.loc.LocUtils;
-
-public class ConfirmThenAlert extends DlgDelegateAlert {
-
- public static ConfirmThenAlert newInstance( DlgState state )
- {
- ConfirmThenAlert result = new ConfirmThenAlert();
- result.addStateArgument( state );
- return result;
- }
-
- public ConfirmThenAlert() {}
-
- @Override
- public void populateBuilder( Context context, DlgState state,
- AlertDialog.Builder builder )
- {
- NotAgainView naView = addNAView( state, builder );
- OnClickListener lstnr = mkCallbackClickListener( naView );
-
- builder.setTitle( state.m_titleId == 0 ? R.string.query_title : state.m_titleId )
- .setPositiveButton( state.m_posButton, lstnr )
- .setNegativeButton( state.m_negButton, lstnr );
-
- if ( null != state.m_pair ) {
- ActionPair pair = state.m_pair;
- builder.setNeutralButton( pair.buttonStr,
- mkCallbackClickListener( pair, naView ) );
- }
- }
-}
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/ConnViaViewLayout.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/ConnViaViewLayout.java
index 85af9863c..60f037ec9 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/ConnViaViewLayout.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/ConnViaViewLayout.java
@@ -181,7 +181,7 @@ public class ConnViaViewLayout extends LinearLayout {
break;
}
- DlgDelegate.DlgDelegateBuilder builder = 0 != keyID
+ DlgDelegate.Builder builder = 0 != keyID
? m_dlgDlgt.makeNotAgainBuilder( msgID, keyID )
: m_dlgDlgt.makeOkOnlyBuilder( msgID )
.setActionPair( Action.PERMS_BANNED_INFO,
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DelegateBase.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DelegateBase.java
index 76dbbe2ff..c4b03c067 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DelegateBase.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DelegateBase.java
@@ -41,10 +41,8 @@ import android.widget.EditText;
import android.widget.TextView;
import org.eehouse.android.xw4.DlgDelegate.Action;
-import org.eehouse.android.xw4.DlgDelegate.ConfirmThenBuilder;
+import org.eehouse.android.xw4.DlgDelegate.Builder;
import org.eehouse.android.xw4.DlgDelegate.DlgClickNotify;
-import org.eehouse.android.xw4.DlgDelegate.NotAgainBuilder;
-import org.eehouse.android.xw4.DlgDelegate.OkOnlyBuilder;
import org.eehouse.android.xw4.MultiService.MultiEvent;
import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType;
import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnTypeSet;
@@ -485,17 +483,13 @@ public class DelegateBase implements DlgClickNotify,
DlgDelegateAlert df = null;
switch ( state.m_id ) {
case CONFIRM_THEN:
- df = ConfirmThenAlert.newInstance( state );
+ case DIALOG_OKONLY:
+ case DIALOG_NOTAGAIN:
+ df = DlgDelegateAlert.newInstance( state );
break;
case DIALOG_ENABLESMS:
df = EnableSMSAlert.newInstance( state );
break;
- case DIALOG_OKONLY:
- df = OkOnlyAlert.newInstance( state );
- break;
- case DIALOG_NOTAGAIN:
- df = NotAgainAlert.newInstance( state );
- break;
case INVITE_CHOICES_THEN:
df = InviteChoicesAlert.newInstance( state );
break;
@@ -521,33 +515,33 @@ public class DelegateBase implements DlgClickNotify,
return LocUtils.makeAlertBuilder( m_activity );
}
- public NotAgainBuilder
+ public Builder
makeNotAgainBuilder( String msg, int key, Action action )
{
return m_dlgDelegate.makeNotAgainBuilder( msg, key, action );
}
- public NotAgainBuilder
+ public Builder
makeNotAgainBuilder( int msgId, int key, Action action )
{
return m_dlgDelegate.makeNotAgainBuilder( msgId, key, action );
}
- public NotAgainBuilder makeNotAgainBuilder( String msg, int key )
+ public Builder makeNotAgainBuilder( String msg, int key )
{
return m_dlgDelegate.makeNotAgainBuilder( msg, key );
}
- public NotAgainBuilder makeNotAgainBuilder( int msgId, int key )
+ public Builder makeNotAgainBuilder( int msgID, int key )
{
- return m_dlgDelegate.makeNotAgainBuilder( msgId, key );
+ return m_dlgDelegate.makeNotAgainBuilder( msgID, key );
}
- public ConfirmThenBuilder makeConfirmThenBuilder( String msg, Action action ) {
+ public Builder makeConfirmThenBuilder( String msg, Action action ) {
return m_dlgDelegate.makeConfirmThenBuilder( msg, action );
}
- public ConfirmThenBuilder makeConfirmThenBuilder( int msgId, Action action ) {
+ public Builder makeConfirmThenBuilder( int msgId, Action action ) {
return m_dlgDelegate.makeConfirmThenBuilder( msgId, action );
}
@@ -578,12 +572,12 @@ public class DelegateBase implements DlgClickNotify,
m_dlgDelegate.showInviteChoicesThen( action, info );
}
- public OkOnlyBuilder makeOkOnlyBuilder( int msgId )
+ public Builder makeOkOnlyBuilder( int msgID )
{
- return m_dlgDelegate.makeOkOnlyBuilder( msgId );
+ return m_dlgDelegate.makeOkOnlyBuilder( msgID );
}
- public OkOnlyBuilder makeOkOnlyBuilder( String msg )
+ public Builder makeOkOnlyBuilder( String msg )
{
return m_dlgDelegate.makeOkOnlyBuilder( msg );
}
@@ -722,12 +716,10 @@ public class DelegateBase implements DlgClickNotify,
final int key = notAgainKey;
runOnUiThread( new Runnable() {
public void run() {
- DlgDelegate.DlgDelegateBuilder builder;
- if ( 0 == key ) {
- builder = makeOkOnlyBuilder( msg );
- } else {
- builder = makeNotAgainBuilder( msg, key );
- }
+ Builder builder = 0 == key
+ ? makeOkOnlyBuilder( msg )
+ : makeNotAgainBuilder( msg, key )
+ ;
builder.show();
}
});
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DlgDelegate.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DlgDelegate.java
index 9877ab87c..27fff5d07 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DlgDelegate.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DlgDelegate.java
@@ -123,10 +123,6 @@ public class DlgDelegate {
ASKED_PHONE_STATE,
PERMS_QUERY,
PERMS_BANNED_INFO,
-
- // Sent when not-again checkbox checked
- SET_NA_DEFAULTNAME,
- SET_GOT_LANGDICT,
} // Action enum
public static class ActionPair implements Serializable {
@@ -154,139 +150,169 @@ public class DlgDelegate {
}
}
- public abstract class DlgDelegateBuilder {
- protected String m_msgString;
- protected int m_prefsNAKey;
- protected Action m_onNA;
- protected int m_posButton = android.R.string.ok;
- protected int m_negButton = android.R.string.cancel;
- protected Action m_action;
- protected Object[] m_params;
- protected int m_titleId = 0;
- protected ActionPair m_actionPair;
+ public class Builder {
+ private final DlgState mState;
- public DlgDelegateBuilder( String msg, Action action )
- { m_msgString = msg; m_action = action; }
+ Builder( DlgID dlgID ) {
+ mState = new DlgState( dlgID )
+ .setPosButton( android.R.string.ok ) // default
+ .setAction( Action.SKIP_CALLBACK )
+ ;
+ }
- public DlgDelegateBuilder( int msgId, Action action )
- { this( getString(msgId), action );}
+ Builder setMessage( String msg )
+ {
+ mState.setMsg( msg );
+ return this;
+ }
- public DlgDelegateBuilder setAction( Action action )
- { m_action = action; return this; }
+ Builder setMessageID( int msgID )
+ {
+ mState.setMsg( getString( msgID ) );
+ return this;
+ }
- public DlgDelegateBuilder setNAKey( int keyId )
- { m_prefsNAKey = keyId; return this; }
+ Builder setActionPair( Action action, int strID )
+ {
+ mState.setActionPair( new ActionPair( action, strID ) );
+ return this;
+ }
- public DlgDelegateBuilder setOnNA( Action onNA )
- { m_onNA = onNA; return this; }
+ Builder setAction( Action action )
+ {
+ mState.setAction( action );
+ return this;
+ }
- public DlgDelegateBuilder setPosButton( int id )
- { m_posButton = id; return this; }
+ Builder setPosButton( int strID )
+ {
+ mState.setPosButton( strID );
+ return this;
+ }
- public DlgDelegateBuilder setNegButton( int id )
- { m_negButton = id; return this; }
+ Builder setNegButton( int strID )
+ {
+ mState.setNegButton( strID );
+ return this;
+ }
- public DlgDelegateBuilder setParams( Object... params )
- { m_params = params; return this; }
+ Builder setTitle( int strID )
+ {
+ mState.setTitle( strID );
+ return this;
+ }
- public DlgDelegateBuilder setTitle( int titleId )
- { m_titleId = titleId; return this; }
+ Builder setParams( Object... params )
+ {
+ mState.setParams( params );
+ return this;
+ }
- public DlgDelegateBuilder setActionPair( ActionPair pr )
- { m_actionPair = pr; return this; }
+ Builder setNAKey( int keyID )
+ {
+ mState.setPrefsNAKey( keyID );
+ return this;
+ }
- public DlgDelegateBuilder setActionPair( Action actn, int id )
- { return setActionPair( new ActionPair( actn, id ) ); }
-
- abstract void show();
- }
-
- public class OkOnlyBuilder extends DlgDelegateBuilder {
-
- public OkOnlyBuilder(String msg) { super( msg, Action.SKIP_CALLBACK ); }
- public OkOnlyBuilder(int msgId) { super( msgId, Action.SKIP_CALLBACK ); }
-
- @Override
public void show()
{
- showOKOnlyDialogThen( m_msgString, m_action, m_actionPair,
- m_params, m_titleId );
+ int naKey = mState.m_prefsNAKey;
+ final Action action = mState.m_action;
+ // Log.d( TAG, "show(): key: %d; action: %s", naKey, action );
+
+ if ( 0 == naKey || ! XWPrefs.getPrefsBoolean( m_activity, naKey, false ) ) {
+ m_dlgt.show( mState );
+ } else if ( Action.SKIP_CALLBACK != action ) {
+ post( new Runnable() {
+ @Override
+ public void run() {
+ Log.d( TAG, "calling onPosButton()" );
+ XWActivity xwact = (XWActivity)m_activity;
+ xwact.onPosButton( action, mState.m_params );
+ }
+ });
+ }
}
}
- public class ConfirmThenBuilder extends DlgDelegateBuilder {
- public ConfirmThenBuilder(String msg, Action action) {super(msg, action);}
- public ConfirmThenBuilder(int msgId, Action action) {super(msgId, action);}
- @Override
- public void show()
- {
- showConfirmThen( m_prefsNAKey, m_onNA, m_msgString, m_posButton,
- m_negButton, m_action, m_titleId, m_actionPair,
- m_params );
- }
- }
-
- public class NotAgainBuilder extends DlgDelegateBuilder {
- public NotAgainBuilder(String msg, int key, Action action)
- { super(msg, action); setNAKey( key ); }
-
- public NotAgainBuilder(int msgId, int key, Action action)
- { super(msgId, action); m_prefsNAKey = key; }
-
- public NotAgainBuilder( String msg, int key )
- { super( msg, Action.SKIP_CALLBACK ); m_prefsNAKey = key; }
-
- public NotAgainBuilder( int msgId, int key )
- { super( msgId, Action.SKIP_CALLBACK ); m_prefsNAKey = key; }
-
- @Override
- public void show()
- {
- showNotAgainDlgThen( m_msgString, m_prefsNAKey,
- m_action, m_actionPair,
- m_params );
- }
- }
-
- public OkOnlyBuilder makeOkOnlyBuilder( String msg )
+ public Builder makeOkOnlyBuilder( String msg )
{
- return new OkOnlyBuilder( msg );
- }
- public OkOnlyBuilder makeOkOnlyBuilder( int msgId )
- {
- return new OkOnlyBuilder( msgId );
+ // return new OkOnlyBuilder( msg );
+
+ Builder builder = new Builder( DlgID.DIALOG_OKONLY )
+ .setMessage( msg )
+ ;
+ return builder;
}
- public ConfirmThenBuilder makeConfirmThenBuilder( String msg, Action action )
+ public Builder makeOkOnlyBuilder( int msgID )
{
- return new ConfirmThenBuilder( msg, action );
+ Builder builder = new Builder( DlgID.DIALOG_OKONLY )
+ .setMessageID( msgID )
+ ;
+ return builder;
}
- public ConfirmThenBuilder makeConfirmThenBuilder(int msgId, Action action)
+ private Builder makeConfirmThenBuilder( Action action )
{
- return new ConfirmThenBuilder( msgId, action );
+ return new Builder( DlgID.CONFIRM_THEN )
+ .setAction( action )
+ .setNegButton( android.R.string.cancel )
+ ;
+ }
+
+ public Builder makeConfirmThenBuilder( String msg, Action action )
+ {
+ return makeConfirmThenBuilder( action )
+ .setMessage( msg )
+ ;
}
- public NotAgainBuilder makeNotAgainBuilder( int msgId, int key,
- Action action )
+ public Builder makeConfirmThenBuilder( int msgID, Action action )
{
- return new NotAgainBuilder( msgId, key, action );
+ return makeConfirmThenBuilder( action )
+ .setMessageID( msgID )
+ ;
}
- public NotAgainBuilder makeNotAgainBuilder( String msg, int key,
- Action action )
+ private Builder makeNotAgainBuilder( int key )
{
- return new NotAgainBuilder( msg, key, action );
+ return new Builder( DlgID.DIALOG_NOTAGAIN )
+ .setNAKey( key )
+ .setAction( Action.SKIP_CALLBACK )
+ .setTitle( R.string.newbie_title )
+ ;
}
- public NotAgainBuilder makeNotAgainBuilder( String msg, int key )
+ public Builder makeNotAgainBuilder( String msg, int key, Action action )
{
- return new NotAgainBuilder( msg, key );
+ return makeNotAgainBuilder( key )
+ .setMessage( msg )
+ .setAction( action )
+ ;
}
- public NotAgainBuilder makeNotAgainBuilder( int msgId, int key ) {
- return new NotAgainBuilder( msgId, key );
+ public Builder makeNotAgainBuilder( int msgID, int key, Action action )
+ {
+ return makeNotAgainBuilder( key )
+ .setMessageID( msgID )
+ .setAction( action )
+ ;
+ }
+
+ public Builder makeNotAgainBuilder( String msg, int key )
+ {
+ return makeNotAgainBuilder( key )
+ .setMessage( msg )
+ ;
+ }
+
+ public Builder makeNotAgainBuilder( int msgID, int key )
+ {
+ return makeNotAgainBuilder( key )
+ .setMessageID( msgID )
+ ;
}
public static final int SMS_BTN = AlertDialog.BUTTON_POSITIVE;
@@ -309,11 +335,10 @@ public class DlgDelegate {
void inviteChoiceMade( Action action, InviteMeans means, Object... params );
}
public interface HasDlgDelegate {
- OkOnlyBuilder makeOkOnlyBuilder( int msgID );
- OkOnlyBuilder makeOkOnlyBuilder( String msg );
- NotAgainBuilder makeNotAgainBuilder( int msgID, int prefsKey,
- Action action );
- NotAgainBuilder makeNotAgainBuilder( int msgID, int prefsKey );
+ Builder makeOkOnlyBuilder( int msgID );
+ Builder makeOkOnlyBuilder( String msg );
+ Builder makeNotAgainBuilder( int msgID, int prefsKey, Action action );
+ Builder makeNotAgainBuilder( int msgID, int prefsKey );
}
private Activity m_activity;
@@ -337,19 +362,6 @@ public class DlgDelegate {
stopProgress();
}
- private void showOKOnlyDialogThen( String msg, Action action,
- ActionPair more, Object[] params,
- int titleId )
- {
- DlgState state = new DlgState( DlgID.DIALOG_OKONLY )
- .setMsg( msg )
- .setParams( params )
- .setTitle( titleId )
- .setActionPair( more )
- .setAction(action);
- m_dlgt.show( state );
- }
-
// Puts up alert asking to choose a reason to enable SMS, and on dismiss
// calls onPosButton/onNegButton with the action and in params a Boolean
// indicating whether enabling is now ok.
@@ -361,62 +373,6 @@ public class DlgDelegate {
m_dlgt.show( state );
}
- private void showNotAgainDlgThen( String msg, int prefsKey,
- final Action action, ActionPair more,
- final Object[] params )
- {
- if ( 0 != prefsKey
- && XWPrefs.getPrefsBoolean( m_activity, prefsKey, false ) ) {
- // If it's set, do the action without bothering with the
- // dialog
- if ( Action.SKIP_CALLBACK != action ) {
- post( new Runnable() {
- public void run() {
- XWActivity xwact = (XWActivity)m_activity;
- xwact.onPosButton( action, params );
- }
- });
- }
- } else {
- DlgState state = new DlgState( DlgID.DIALOG_NOTAGAIN )
- .setMsg( msg)
- .setPrefsKey( prefsKey )
- .setAction( action )
- .setActionPair( more )
- .setParams( params );
- m_dlgt.show( state );
- }
- }
-
- private void showConfirmThen( int nakey, Action onNA, String msg,
- int posButton, int negButton,
- final Action action, int titleId,
- ActionPair more, final Object[] params )
- {
- if ( 0 == nakey ||
- ! XWPrefs.getPrefsBoolean( m_activity, nakey, false ) ) {
- DlgState state = new DlgState( DlgID.CONFIRM_THEN )
- .setOnNA( onNA )
- .setMsg( msg )
- .setPosButton( posButton )
- .setNegButton( negButton )
- .setAction( action )
- .setTitle( titleId )
- .setActionPair( more )
- .setParams( params )
- .setPrefsKey( nakey )
- ;
- m_dlgt.show( state );
- } else if ( Action.SKIP_CALLBACK != action ) {
- post( new Runnable() {
- public void run() {
- XWActivity xwact = (XWActivity)m_activity;
- xwact.onDismissed( action, params );
- }
- });
- }
- }
-
public void showInviteChoicesThen( final Action action,
SentInvitesInfo info )
{
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DlgDelegateAlert.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DlgDelegateAlert.java
index 03d0980b0..eee10f207 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DlgDelegateAlert.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DlgDelegateAlert.java
@@ -1,6 +1,7 @@
-/* -*- compile-command: "find-and-gradle.sh inXw4dDebug"; -*- */
+/* -*- compile-command: "find-and-gradle.sh inXw4dDeb"; -*- */
/*
- * Copyright 2017 by Eric House (xwords@eehouse.org). All rights reserved.
+ * Copyright 2017 - 2020 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
@@ -37,13 +38,20 @@ import org.eehouse.android.xw4.loc.LocUtils;
/** Abstract superclass for Alerts that have moved from and are still created
* inside DlgDelegate
*/
-abstract class DlgDelegateAlert extends XWDialogFragment {
+public class DlgDelegateAlert extends XWDialogFragment {
private static final String TAG = DlgDelegateAlert.class.getSimpleName();
private static final String STATE_KEY = "STATE_KEY";
private DlgState m_state;
public DlgDelegateAlert() {}
+ public static DlgDelegateAlert newInstance( DlgState state )
+ {
+ DlgDelegateAlert result = new DlgDelegateAlert();
+ result.addStateArgument( state );
+ return result;
+ }
+
protected final DlgState getState( Bundle sis )
{
if ( m_state == null ) {
@@ -63,18 +71,37 @@ abstract class DlgDelegateAlert extends XWDialogFragment {
setArguments( state.toBundle() );
}
- abstract void populateBuilder( Context context, DlgState state,
- AlertDialog.Builder builder );
+ protected void populateBuilder( Context context, DlgState state,
+ AlertDialog.Builder builder )
+ {
+ Log.d( TAG, "populateBuilder()" );
+ NotAgainView naView = addNAView( state, builder );
+
+ OnClickListener lstnr = mkCallbackClickListener( naView );
+ if ( 0 != state.m_posButton ) {
+ builder.setPositiveButton( state.m_posButton, lstnr );
+ }
+ if ( 0 != state.m_negButton ) {
+ builder.setNegativeButton( state.m_negButton, lstnr );
+ }
+
+ if ( null != state.m_pair ) {
+ ActionPair pair = state.m_pair;
+ builder.setNeutralButton( pair.buttonStr,
+ mkCallbackClickListener( pair, naView ) );
+ }
+ }
Dialog create( AlertDialog.Builder builder ) { return builder.create(); }
- protected NotAgainView addNAView( DlgState state, AlertDialog.Builder builder )
+ private NotAgainView addNAView( DlgState state, AlertDialog.Builder builder )
{
Context context = getActivity();
NotAgainView naView =
((NotAgainView)LocUtils.inflate( context, R.layout.not_again_view ))
.setMessage( state.m_msg )
- .setShowNACheckbox( 0 != state.m_prefsNAKey );
+ .setShowNACheckbox( 0 != state.m_prefsNAKey )
+ ;
builder.setView( naView );
@@ -89,6 +116,10 @@ abstract class DlgDelegateAlert extends XWDialogFragment {
AlertDialog.Builder builder = LocUtils.makeAlertBuilder( context );
+ if ( 0 != state.m_titleId ) {
+ builder.setTitle( state.m_titleId );
+ }
+
populateBuilder( context, state, builder );
return create( builder );
@@ -131,9 +162,6 @@ abstract class DlgDelegateAlert extends XWDialogFragment {
if ( 0 != state.m_prefsNAKey ) {
XWPrefs.setPrefsBoolean( getActivity(), m_state.m_prefsNAKey,
true );
- } else if ( null != state.m_onNAChecked ) {
- DlgClickNotify notify = (DlgClickNotify)getActivity();
- notify.onPosButton( m_state.m_onNAChecked );
}
}
}
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DlgState.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DlgState.java
index 8ec5603b8..a0f3a4360 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DlgState.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DlgState.java
@@ -46,7 +46,6 @@ public class DlgState implements Parcelable {
public int m_prefsNAKey;
// These can't be serialized!!!!
public Object[] m_params;
- public Action m_onNAChecked;
public int m_titleId;
public DlgState( DlgID dlgID )
@@ -56,7 +55,7 @@ public class DlgState implements Parcelable {
public DlgState setMsg( String msg )
{ m_msg = msg; return this; }
- public DlgState setPrefsKey( int key )
+ public DlgState setPrefsNAKey( int key )
{ m_prefsNAKey = key; return this; }
public DlgState setAction( Action action )
{ m_action = action; return this; }
@@ -76,8 +75,6 @@ public class DlgState implements Parcelable {
}
public DlgState setActionPair( ActionPair pair )
{ m_pair = pair; return this; }
- public DlgState setOnNA( Action na )
- { m_onNAChecked = na; return this; }
public DlgState setPosButton( int id )
{ m_posButton = id; return this; }
public DlgState setNegButton( int id )
@@ -88,6 +85,7 @@ public class DlgState implements Parcelable {
@Override
public String toString()
{
+ String result;
if ( BuildConfig.DEBUG) {
String params = "";
if ( null != m_params ) {
@@ -97,14 +95,22 @@ public class DlgState implements Parcelable {
}
params = TextUtils.join( ",", strs );
}
- return String.format("[id: %s; msg: %s; key: %s; action: %s; pair %s; "
- + "na: %s; pos: %d; neg: %d; title: %d; "
- + "params: %s]",
- m_id, m_msg, m_prefsNAKey, m_action, m_pair, m_onNAChecked,
- m_posButton, m_negButton, m_titleId, params );
+ result = new StringBuffer()
+ .append("{id: ").append(m_id)
+ .append(", msg: \"").append(m_msg)
+ .append("\", naKey: ").append(m_prefsNAKey)
+ .append(", action: ").append(m_action)
+ .append(", pair ").append(m_pair)
+ .append(", pos: ").append(m_posButton)
+ .append(", neg: ").append(m_negButton)
+ .append(", title: ").append(m_titleId)
+ .append(", params: [").append(params)
+ .append("]}")
+ .toString();
} else {
- return super.toString();
+ result = super.toString();
}
+ return result;
}
// I only need this if BuildConfig.DEBUG is true...
@@ -125,7 +131,6 @@ public class DlgState implements Parcelable {
&& ((null == m_pair) ? (null == other.m_pair) : m_pair.equals(other.m_pair))
&& m_prefsNAKey == other.m_prefsNAKey
&& Arrays.deepEquals( m_params, other.m_params )
- && m_onNAChecked == other.m_onNAChecked
&& m_titleId == other.m_titleId;
}
} else {
@@ -159,7 +164,6 @@ public class DlgState implements Parcelable {
out.writeInt( m_negButton );
out.writeInt( null == m_action ? -1 : m_action.ordinal() );
out.writeInt( m_prefsNAKey );
- out.writeInt( null == m_onNAChecked ? -1 : m_onNAChecked.ordinal() );
out.writeInt( m_titleId );
out.writeString( m_msg );
out.writeSerializable( m_params );
@@ -168,7 +172,7 @@ public class DlgState implements Parcelable {
private void testCanParcelize()
{
- if (BuildConfig.DEBUG) {
+ if ( BuildConfig.DEBUG ) {
Parcel parcel = Parcel.obtain();
writeToParcel(parcel, 0);
@@ -192,8 +196,6 @@ public class DlgState implements Parcelable {
int tmp = in.readInt();
Action action = 0 > tmp ? null : Action.values()[tmp];
int prefsKey = in.readInt();
- tmp = in.readInt();
- Action onNA = 0 > tmp ? null : Action.values()[tmp];
int titleId = in.readInt();
String msg = in.readString();
Object[] params = (Object[])in.readSerializable();
@@ -203,8 +205,7 @@ public class DlgState implements Parcelable {
.setPosButton( posButton )
.setNegButton( negButton )
.setAction( action )
- .setPrefsKey( prefsKey )
- .setOnNA( onNA )
+ .setPrefsNAKey( prefsKey )
.setTitle(titleId)
.setParams(params)
.setActionPair(pair)
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/GameListItem.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/GameListItem.java
index b3050fcf6..3c539a9b5 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/GameListItem.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/GameListItem.java
@@ -462,6 +462,7 @@ public class GameListItem extends LinearLayout
}
long m_rowid;
GameListItem m_item;
+ int m_nTries = 0;
}
private static LinkedBlockingQueue s_queue
= new LinkedBlockingQueue<>();
@@ -489,6 +490,11 @@ public class GameListItem extends LinearLayout
ThumbQueueElem elem;
try {
elem = s_queue.take();
+ if ( 0 < elem.m_nTries ) {
+ // This is a second pass. give whatever caused
+ // failure time to go away
+ Thread.sleep(200);
+ }
} catch ( InterruptedException ie ) {
Log.w( TAG, "interrupted; killing s_thumbThread" );
break;
@@ -501,16 +507,22 @@ public class GameListItem extends LinearLayout
thumb = GameUtils.loadMakeBitmap( activity, rowid );
}
- final GameListItem item = elem.m_item;
- final Bitmap ft = thumb;
- activity.runOnUiThread( new Runnable() {
- public void run() {
- ImageView iview = item.m_thumb;
- if ( null != iview ) {
- iview.setImageBitmap( ft );
+ if ( null == thumb ) {
+ if ( ++elem.m_nTries < 3 ) {
+ s_queue.add( elem );
+ }
+ } else {
+ final GameListItem item = elem.m_item;
+ final Bitmap ft = thumb;
+ activity.runOnUiThread( new Runnable() {
+ public void run() {
+ ImageView iview = item.m_thumb;
+ if ( null != iview ) {
+ iview.setImageBitmap( ft );
+ }
}
- }
- });
+ });
+ }
}
}
});
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/GamesListDelegate.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/GamesListDelegate.java
index 85fa80846..53497baaf 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/GamesListDelegate.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/GamesListDelegate.java
@@ -1452,15 +1452,6 @@ public class GamesListDelegate extends ListDelegateBase
}
break;
- case SET_NA_DEFAULTNAME:
- XWPrefs.setPrefsBoolean( m_activity, R.string.key_notagain_dfltname,
- true );
- break;
- case SET_GOT_LANGDICT:
- XWPrefs.setPrefsBoolean( m_activity, R.string.key_got_langdict,
- true );
- break;
-
default:
handled = super.onPosButton( action, params );
}
@@ -2001,7 +1992,7 @@ public class GamesListDelegate extends ListDelegateBase
// DEBUG only
case R.id.games_game_markbad:
- Quarantine.recordOpened( selRowIDs[0] );
+ Quarantine.markBad( selRowIDs[0] );
break;
default:
@@ -2438,7 +2429,7 @@ public class GamesListDelegate extends ListDelegateBase
boolean handled = false;
String msg = intent.getStringExtra( ALERT_MSG );
if ( null != msg ) {
- DlgDelegate.DlgDelegateBuilder builder =
+ DlgDelegate.Builder builder =
makeOkOnlyBuilder( msg );
if ( intent.getBooleanExtra( WITH_EMAIL, false ) ) {
builder.setActionPair( Action.SEND_EMAIL,
@@ -2497,7 +2488,8 @@ public class GamesListDelegate extends ListDelegateBase
xlateLang( lang ) );
makeConfirmThenBuilder( msg, Action.DWNLD_LOC_DICT )
.setPosButton( R.string.button_download )
- .setOnNA( Action.SET_GOT_LANGDICT )
+ .setNegButton( R.string.button_no )
+ .setNAKey( R.string.key_got_langdict )
.setParams( lang, name )
.show();
}
@@ -2754,7 +2746,7 @@ public class GamesListDelegate extends ListDelegateBase
String msg = getQuantityString( R.plurals.confirm_reset_fmt,
rowIDs.length, rowIDs.length );
makeConfirmThenBuilder( msg, Action.RESET_GAMES )
- .setPosButton(R.string.button_reset)
+ .setPosButton( R.string.button_reset )
.setParams( rowIDs )
.show();
}
@@ -2798,7 +2790,7 @@ public class GamesListDelegate extends ListDelegateBase
name2 );
makeConfirmThenBuilder( msg, Action.NEW_GAME_DFLT_NAME )
- .setOnNA( Action.SET_NA_DEFAULTNAME )
+ .setNAKey( R.string.key_notagain_dfltname )
.setNegButton( R.string.button_later )
.setParams( name, doConfigure )
.show();
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/NotAgainAlert.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/NotAgainAlert.java
deleted file mode 100644
index a4a49e139..000000000
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/NotAgainAlert.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/* -*- compile-command: "find-and-gradle.sh inXw4dDebug"; -*- */
-/*
- * Copyright 2017 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
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-package org.eehouse.android.xw4;
-
-import android.app.AlertDialog;
-import android.content.Context;
-
-import org.eehouse.android.xw4.DlgDelegate.ActionPair;
-
-public class NotAgainAlert extends DlgDelegateAlert {
- private static final String TAG = NotAgainAlert.class.getSimpleName();
-
- public static NotAgainAlert newInstance( DlgState state )
- {
- NotAgainAlert result = new NotAgainAlert();
- result.addStateArgument( state );
- return result;
- }
-
- public NotAgainAlert() {}
-
- @Override
- public void populateBuilder( Context context, DlgState state,
- AlertDialog.Builder builder )
- {
- NotAgainView naView = addNAView( state, builder );
- builder.setTitle( R.string.newbie_title )
- .setPositiveButton( android.R.string.ok,
- mkCallbackClickListener( naView ) );
-
- if ( null != state.m_pair ) {
- ActionPair pair = state.m_pair;
- builder.setNegativeButton( pair.buttonStr,
- mkCallbackClickListener( pair, naView ) );
- }
- }
-}
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/OkOnlyAlert.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/OkOnlyAlert.java
deleted file mode 100644
index 32f49f0a0..000000000
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/OkOnlyAlert.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/* -*- compile-command: "find-and-gradle.sh inXw4dDebug"; -*- */
-/*
- * Copyright 2017 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
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-package org.eehouse.android.xw4;
-
-import android.app.AlertDialog;
-import android.content.Context;
-
-import org.eehouse.android.xw4.DlgDelegate.ActionPair;
-
-public class OkOnlyAlert extends DlgDelegateAlert {
- private static final String TAG = OkOnlyAlert.class.getSimpleName();
-
- public static OkOnlyAlert newInstance( DlgState state )
- {
- OkOnlyAlert result = new OkOnlyAlert();
- result.addStateArgument( state );
- return result;
- }
-
- public OkOnlyAlert() {}
-
- @Override
- public void populateBuilder( Context context, DlgState state,
- AlertDialog.Builder builder )
- {
- builder.setTitle( state.m_titleId == 0 ? R.string.info_title : state.m_titleId )
- .setMessage( state.m_msg )
- .setPositiveButton( android.R.string.ok, null )
- ;
-
- ActionPair pair = state.m_pair;
- if ( null != pair ) {
- builder.setNeutralButton( pair.buttonStr, mkCallbackClickListener( pair ) );
- }
- }
-}
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/PrefsActivity.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/PrefsActivity.java
index ea61b4747..fd4c2b45b 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/PrefsActivity.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/PrefsActivity.java
@@ -26,11 +26,8 @@ import android.content.Intent;
import android.os.Bundle;
import android.preference.PreferenceActivity;
-
import org.eehouse.android.xw4.DlgDelegate.Action;
-import org.eehouse.android.xw4.DlgDelegate.ConfirmThenBuilder;
-import org.eehouse.android.xw4.DlgDelegate.NotAgainBuilder;
-import org.eehouse.android.xw4.DlgDelegate.OkOnlyBuilder;
+import org.eehouse.android.xw4.DlgDelegate.Builder;
import org.eehouse.android.xw4.loc.LocUtils;
public class PrefsActivity extends PreferenceActivity
@@ -102,32 +99,32 @@ public class PrefsActivity extends PreferenceActivity
m_dlgt.onActivityResult( rc, resultCode, data );
}
- public OkOnlyBuilder makeOkOnlyBuilder( int msgId )
+ public Builder makeOkOnlyBuilder( int msgID )
{
- return m_dlgt.makeOkOnlyBuilder( msgId );
+ return m_dlgt.makeOkOnlyBuilder( msgID );
}
- public OkOnlyBuilder makeOkOnlyBuilder( String msg )
+ public Builder makeOkOnlyBuilder( String msg )
{
return m_dlgt.makeOkOnlyBuilder( msg );
}
- public NotAgainBuilder makeNotAgainBuilder(int msgId, int key, Action action)
+ public Builder makeNotAgainBuilder(int msgID, int key, Action action)
{
- return m_dlgt.makeNotAgainBuilder( msgId, key, action );
+ return m_dlgt.makeNotAgainBuilder( msgID, key, action );
}
- public NotAgainBuilder makeNotAgainBuilder( int msgId, int key )
+ public Builder makeNotAgainBuilder( int msgID, int key )
{
- return m_dlgt.makeNotAgainBuilder( msgId, key );
+ return m_dlgt.makeNotAgainBuilder( msgID, key );
}
- public ConfirmThenBuilder makeConfirmThenBuilder(String msg, Action action)
+ public Builder makeConfirmThenBuilder( String msg, Action action )
{
return m_dlgt.makeConfirmThenBuilder( msg, action );
}
- public ConfirmThenBuilder makeConfirmThenBuilder(int msgID, Action action)
+ public Builder makeConfirmThenBuilder( int msgID, Action action )
{
return m_dlgt.makeConfirmThenBuilder( msgID, action );
}
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/PrefsDelegate.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/PrefsDelegate.java
index 9abb91d43..97f5c2e56 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/PrefsDelegate.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/PrefsDelegate.java
@@ -102,6 +102,7 @@ public class PrefsDelegate extends DelegateBase
R.string.key_empty,
R.string.key_background,
R.string.key_clr_bonushint,
+ R.string.key_cellline,
};
for ( int colorKey : colorKeys ) {
editor.remove( getString(colorKey) );
@@ -242,7 +243,9 @@ public class PrefsDelegate extends DelegateBase
case R.string.key_mqtt_host:
case R.string.key_mqtt_port:
case R.string.key_mqtt_qos:
- MQTTUtils.onConfigChanged( m_activity );
+ if ( BuildConfig.OFFER_MQTT ) {
+ MQTTUtils.onConfigChanged( m_activity );
+ }
break;
default:
Assert.failDbg();
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/Quarantine.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/Quarantine.java
index 43726258f..dc52ea53b 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/Quarantine.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/Quarantine.java
@@ -30,6 +30,7 @@ import java.io.Serializable;
public class Quarantine {
private static final String TAG = Quarantine.class.getSimpleName();
private static final String DATA_KEY = TAG + "/key";
+ private static final int BAD_COUNT = 2;
public static boolean safeToOpen( long rowid )
{
@@ -37,7 +38,7 @@ public class Quarantine {
synchronized ( sDataRef ) {
count = get().getFor( rowid );
}
- boolean result = count == 0; // Not too strict?
+ boolean result = count < BAD_COUNT;
if ( !result ) {
Log.d( TAG, "safeToOpen(%d) => %b (count=%d)", rowid, result, count );
}
@@ -70,6 +71,17 @@ public class Quarantine {
}
}
+ public static void markBad( long rowid )
+ {
+ synchronized ( sDataRef ) {
+ for ( int ii = 0; ii < BAD_COUNT; ++ii ) {
+ get().increment( rowid );
+ }
+ store();
+ Log.d( TAG, "markBad(%d): %s", rowid, sDataRef[0].toString() );
+ }
+ }
+
private static class Data implements Serializable {
private HashMap mCounts = new HashMap<>();
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/XWActivity.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/XWActivity.java
index 275de7ef7..6bbdfbadb 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/XWActivity.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/XWActivity.java
@@ -204,23 +204,22 @@ public class XWActivity extends FragmentActivity
// This are a hack! I need some way to build fragment-based alerts from
// inside fragment-based alerts.
- public DlgDelegate.NotAgainBuilder makeNotAgainBuilder( String msg, int keyId )
+ public DlgDelegate.Builder makeNotAgainBuilder( String msg, int keyID )
{
- return m_dlgt.makeNotAgainBuilder( msg, keyId );
+ return m_dlgt.makeNotAgainBuilder( msg, keyID );
}
- public DlgDelegate.NotAgainBuilder makeNotAgainBuilder( int msgID, int keyId )
+ public DlgDelegate.Builder makeNotAgainBuilder( int msgID, int keyID )
{
- return m_dlgt.makeNotAgainBuilder( msgID, keyId );
+ return m_dlgt.makeNotAgainBuilder( msgID, keyID );
}
- public DlgDelegate.ConfirmThenBuilder makeConfirmThenBuilder( int msgID,
- Action action )
+ public DlgDelegate.Builder makeConfirmThenBuilder( int msgID, Action action )
{
return m_dlgt.makeConfirmThenBuilder( msgID, action );
}
- public DlgDelegate.OkOnlyBuilder makeOkOnlyBuilder( int msgID )
+ public DlgDelegate.Builder makeOkOnlyBuilder( int msgID )
{
return m_dlgt.makeOkOnlyBuilder( msgID );
}
@@ -288,7 +287,7 @@ public class XWActivity extends FragmentActivity
df.show( fm, tag );
}
} catch (IllegalStateException ise ) {
- Log.d( TAG, "error showing tag %s (df: %s)", tag, df );
+ Log.d( TAG, "error showing tag %s (df: %s; msg: %s)", tag, df, ise );
// DLG_SCORES is causing this for non-belongsOnBackStack() case
// Assert.assertFalse( BuildConfig.DEBUG );
}
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/XWApp.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/XWApp.java
index 4528b4884..c0eb7b352 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/XWApp.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/XWApp.java
@@ -102,7 +102,9 @@ public class XWApp extends Application
DupeModeTimer.init( this );
- MQTTUtils.init( this );
+ if ( BuildConfig.OFFER_MQTT ) {
+ MQTTUtils.init( this );
+ }
}
@OnLifecycleEvent(ON_ANY)
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/XWPrefs.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/XWPrefs.java
index 096b3a3ec..6f5e342c8 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/XWPrefs.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/XWPrefs.java
@@ -207,19 +207,20 @@ public class XWPrefs {
public static int getPrefsInt( Context context, int keyID, int defaultValue )
{
- String key = context.getString( keyID );
- SharedPreferences sp = PreferenceManager
- .getDefaultSharedPreferences( context );
- int result;
- try {
- result = sp.getInt( key, defaultValue );
- // If it's in a pref, it'll be a string (editable) So will get CCE
- } catch ( ClassCastException cce ) {
- String asStr = sp.getString( key, String.format( "%d", defaultValue ) );
+ int result = defaultValue;
+ if ( null != context ) {
+ String key = context.getString( keyID );
+ SharedPreferences sp = PreferenceManager
+ .getDefaultSharedPreferences( context );
try {
- result = Integer.parseInt( asStr );
- } catch ( Exception ex ) {
- result = defaultValue;
+ result = sp.getInt( key, defaultValue );
+ // If it's in a pref, it'll be a string (editable) So will get CCE
+ } catch ( ClassCastException cce ) {
+ String asStr = sp.getString( key, String.format( "%d", defaultValue ) );
+ try {
+ result = Integer.parseInt( asStr );
+ } catch ( Exception ex ) {
+ }
}
}
return result;
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/CommonPrefs.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/CommonPrefs.java
index c1014ab3a..52b986855 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/CommonPrefs.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/CommonPrefs.java
@@ -40,7 +40,8 @@ public class CommonPrefs extends XWPrefs {
public static final int COLOR_FOCUS = 2;
public static final int COLOR_BACKGRND = 3;
public static final int COLOR_BONUSHINT = 4;
- public static final int COLOR_LAST = 5;
+ public static final int COLOR_CELLLINE = 5;
+ public static final int COLOR_LAST = 6;
private static CommonPrefs s_cp = null;
@@ -108,6 +109,7 @@ public class CommonPrefs extends XWPrefs {
R.string.key_clr_crosshairs,
R.string.key_background,
R.string.key_clr_bonushint,
+ R.string.key_cellline,
};
for ( int ii = 0; ii < idsOther.length; ++ii ) {
otherColors[ii] = prefToColor( context, sp, idsOther[ii] );
diff --git a/xwords4/android/app/src/main/res/drawable/ic_uc.xml b/xwords4/android/app/src/main/res/drawable/ic_uc.xml
deleted file mode 100644
index 4738487ca..000000000
--- a/xwords4/android/app/src/main/res/drawable/ic_uc.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
diff --git a/xwords4/android/app/src/main/res/layout/game_list_item.xml b/xwords4/android/app/src/main/res/layout/game_list_item.xml
index 8d038950c..6d2d35847 100644
--- a/xwords4/android/app/src/main/res/layout/game_list_item.xml
+++ b/xwords4/android/app/src/main/res/layout/game_list_item.xml
@@ -66,7 +66,6 @@
key_clr_tile_back
key_clr_empty
key_clr_background
+ key_cellline
key_clr_crosshairs
key_clr_bonushint
+ key_board_line_pct
key_relay_host
key_relay_port2
diff --git a/xwords4/android/app/src/main/res/values/strings.xml b/xwords4/android/app/src/main/res/values/strings.xml
index 4fe38432d..10518d42e 100644
--- a/xwords4/android/app/src/main/res/values/strings.xml
+++ b/xwords4/android/app/src/main/res/values/strings.xml
@@ -850,6 +850,9 @@
Keep screen on
Keep board screen on 10 mins
+
+
+ Cell line thickness (percent, limit 1 to 25)
In-square bonus hint
+
+ Board lines
Toolbar icons by Sarah Chu. Navbar
icons from the Noun Project: \"archive\" by Trendy; \"rematch\" by
- Becris; and \"swap\" by iconomania. \"Under Construction\" icon by
- ultimatearm from flaticon.com
+ Becris; and \"swap\" by iconomania.
Recent changes
@@ -2058,7 +2062,7 @@
wordlists and view the ones you already have.\n\nWhat wordlists
you have installed determines:\n• What languages you can play
in\n• How smart the robot player is\n• What words are
- legal.\n\nCheck the \"Show downloadable\" box at the top to see
+ legal\n\nCheck the \"Show downloadable\" box at the top to see
what\'s available.
Use tablet (side-by-side) layout?
Use default for my device
diff --git a/xwords4/android/app/src/main/res/xml/xwprefs.xml b/xwords4/android/app/src/main/res/xml/xwprefs.xml
index 160bc4f77..b95da5fc4 100644
--- a/xwords4/android/app/src/main/res/xml/xwprefs.xml
+++ b/xwords4/android/app/src/main/res/xml/xwprefs.xml
@@ -241,8 +241,21 @@
android:title="@string/background"
android:defaultValue="0xFFFFFF"
/>
+
+
+
+
+
+
+
+
+
-
diff --git a/xwords4/common/device.c b/xwords4/common/device.c
index d6b8f541d..f547a72d7 100644
--- a/xwords4/common/device.c
+++ b/xwords4/common/device.c
@@ -81,7 +81,9 @@ dvc_store( XW_DUtilCtxt* dutil, XWEnv xwe )
#endif
-#define MQTT_DEVID_KEY "mqtt_devid_key"
+#define SUPPORT_OLD /* only needed for a short while */
+#define MQTT_DEVID_KEY_OLD "mqtt_devid_key"
+#define MQTT_DEVID_KEY PERSIST_KEY("mqtt_devid_key")
void
dvc_getMQTTDevID( XW_DUtilCtxt* dutil, XWEnv xwe, MQTTDevID* devID )
@@ -89,8 +91,20 @@ dvc_getMQTTDevID( XW_DUtilCtxt* dutil, XWEnv xwe, MQTTDevID* devID )
MQTTDevID tmp = 0;
XP_U16 len = sizeof(tmp);
dutil_loadPtr( dutil, xwe, MQTT_DEVID_KEY, &tmp, &len );
+
+#ifdef SUPPORT_OLD
+ if ( len == 0 ) {
+ len = sizeof(tmp);
+ dutil_loadPtr( dutil, xwe, MQTT_DEVID_KEY_OLD, &tmp, &len );
+ if ( len == sizeof(tmp) ) { /* got the old key; now store it */
+ XP_LOGFF( "storing using new key" );
+ dutil_storePtr( dutil, xwe, MQTT_DEVID_KEY, &tmp, sizeof(tmp) );
+ }
+ }
+#endif
+
// XP_LOGFF( "len: %d; sizeof(tmp): %d", len, sizeof(tmp) );
- if ( len != sizeof(tmp) ) { /* we have it!!! */
+ if ( len != sizeof(tmp) ) { /* not found, or bogus somehow */
tmp = XP_RANDOM();
tmp <<= 32;
tmp |= XP_RANDOM();
diff --git a/xwords4/dawg/English/Makefile.CollegeEng b/xwords4/dawg/English/Makefile.CollegeEng
index 2f360bead..7547a7cbb 100644
--- a/xwords4/dawg/English/Makefile.CollegeEng
+++ b/xwords4/dawg/English/Makefile.CollegeEng
@@ -1,5 +1,6 @@
# -*- mode: makefile; compile-command: "make -f Makefile.CollegeEng"; -*-
-# Copyright 2002 by Eric House (xwords@eehouse.org). All rights reserved.
+# Copyright 2002 - 2020 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
@@ -22,10 +23,10 @@ DICTNOTE = "From the old PalmOS app Niggle"
include ../Makefile.langcommon
-SOURCEDICT ?= $(XWDICTPATH)/English/CollegeEng.dict.gz
+SOURCEDICT ?= $(XWDICTPATH)/English/CollegeEng.dict
$(XWLANG)Main.dict.gz: $(SOURCEDICT) Makefile
- zcat $< | tr -d '\r' | tr [a-z] [A-Z] | grep -e "^[A-Z]\{2,15\}$$" | \
+ cat $< | tr -d '\r' | tr [a-z] [A-Z] | grep -e "^[A-Z]\{2,15\}$$" | \
gzip -c > $@
# Everything but creating of the Main.dict file is inherited from the
diff --git a/xwords4/dawg/Hungarian/info.txt b/xwords4/dawg/Hungarian/info.txt
index 3768603a9..eab5f0148 100644
--- a/xwords4/dawg/Hungarian/info.txt
+++ b/xwords4/dawg/Hungarian/info.txt
@@ -3,6 +3,20 @@
LANGCODE:hu_HU
CHARSET: utf-8
+BUILD_FLAGS:ALLOWS_DUPLICATES
+
+LANGFILTER: tr -d '\r'
+LANGFILTER: | tr [aábcdeéfghiíjklmnnyoóöőprtuúüűvzs] [AÁBCDEÉFGHIÍJKLMNNYOÓÖŐPRTUÚÜŰVZS]
+LANGFILTER: | sed -e 's,^\(.*\)CS\(.*\)$,\11\2\n\1CS\2,g'
+LANGFILTER: | sed -e 's,GY,2,g'
+LANGFILTER: | sed -e 's,LY,3,g'
+LANGFILTER: | sed -e 's,NY,4,g'
+LANGFILTER: | sed -e 's,^\(.*\)SZ\(.*\)$,\15\2\n\1SZ\2,g'
+LANGFILTER: | sed -e 's,TY,6,g'
+LANGFILTER: | sed -e 's,^\(.*\)ZS\(.*\)$,\17\2\n\1ZS\2,g'
+LANGFILTER: | grep '^[1-7AÁBCDEÉFGHIÍJKLMNOÓÖŐPRSTUÚÜŰVZ]\{2,15\}$'
+LANGFILTER: | tr '1234567' '\001\002\003\004\005\006\007'
+
# High bit means "official". Next 7 bits are an enum where
# Hungarian==0x14. Low byte is padding
XLOC_HEADER:0x9400
diff --git a/xwords4/dawg/Italian/Makefile b/xwords4/dawg/Italian/Makefile
deleted file mode 100644
index 7b876d008..000000000
--- a/xwords4/dawg/Italian/Makefile
+++ /dev/null
@@ -1,32 +0,0 @@
-# -*-mode: Makefile -*-
-# Copyright 2002-2004 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 published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-XWLANG=Italian
-LANGCODE=it_IT
-
-TARGET_TYPE ?= WINCE
-
-include ../Makefile.langcommon
-
-$(XWLANG)Main.dict.gz: $(XWDICTPATH)/Italian/ITALIANO.txt.gz
- zcat $< | tr a-z A-Z | grep '^[A-IL-VZ]*$$' | gzip >$@
-
-# Everything but creating of the Main.dict file is inherited from the
-# "parent" Makefile.langcommon in the parent directory.
-
-clean: clean_common
- rm -f $(XWLANG)Main.dict.gz *.bin $(XWLANG)*.pdb $(XWLANG)*.seb
diff --git a/xwords4/dawg/Italian/Makefile b/xwords4/dawg/Italian/Makefile
new file mode 120000
index 000000000..47350dba5
--- /dev/null
+++ b/xwords4/dawg/Italian/Makefile
@@ -0,0 +1 @@
+./Makefile.zinga
\ No newline at end of file
diff --git a/xwords4/dawg/Makefile b/xwords4/dawg/Makefile
index 8e0623956..736a65152 100644
--- a/xwords4/dawg/Makefile
+++ b/xwords4/dawg/Makefile
@@ -23,7 +23,7 @@ byodfiles.tgz: byodfiles.tar
byodfiles.tar: dict2dawg
rm -f $@ langinfo
- tar cvf $@ ./dict2dawg ./dict2dawg.cpp ./par.pl ./xloc.pl ./xloc.pm
+ tar cvf $@ ./dict2dawg ./dict2dawg.cpp ./par.pl ./xloc.py
for dir in $$(ls .); do \
if [ $$dir = "Hëx" ]; then \
:; \
diff --git a/xwords4/dawg/Makefile.langcommon b/xwords4/dawg/Makefile.langcommon
index 04471c625..549b04b31 100644
--- a/xwords4/dawg/Makefile.langcommon
+++ b/xwords4/dawg/Makefile.langcommon
@@ -16,9 +16,11 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-ifndef XWDICTPATH
-$(error XWDICTPATH is not set)
-endif
+# This breaks generating byod files on otherwise-unconfigured
+# machines. Move it perhaps?
+# ifndef XWDICTPATH
+# $(error XWDICTPATH is not set)
+# endif
ifneq ($(DIRTY_LIST),)
BOWDLERIZER = ../remove-dirty.py $(DIRTY_LIST)
@@ -187,12 +189,13 @@ $(XWLANG)%.atts: #recreate it each time based on params
echo '_PERM|global+read-only|"r"' >> $@
# the files to export for byod
-byodbins: table.bin values.bin frankspecials.bin info.txt
-
+byodbins: table.bin charcount.bin values.bin frankspecials.bin info.txt
else
ifeq ($(TARGET_TYPE),WINCE)
+byodbins: table.bin charcount.bin values.bin frankspecials.bin info.txt
+
### WINCE section here ###
all: $(XWLANG)2to8.xwd $(XWLANG)2to9.xwd $(XWLANG)2to15.xwd
../mkxwdcab.pl -f $<
@@ -213,11 +216,11 @@ endif
# For each entry in the table whose face < 32, there needs to be a pair of
# pbitm files and a string giving the printing form
-frankspecials.bin: ../frank_mkspecials.pl $(BMPFILES)
+frankspecials.bin: ../frank_mkspecials.py $(BMPFILES)
$< $(BLANK_INFO) $(LANG_SPECIAL_INFO) > $@
-$(XWLANG)%.$(FRANK_EXT): dawg$(XWLANG)%.stamp $(XWLANG)%_flags.bin $(XWLANG)%_newheader.bin $(XWLANG)_charcount.bin table.bin values.bin frankspecials.bin
- cat $(XWLANG)$*_flags.bin $(XWLANG)$*_newheader.bin $(XWLANG)_charcount.bin table.bin values.bin \
+$(XWLANG)%.$(FRANK_EXT): dawg$(XWLANG)%.stamp $(XWLANG)%_flags.bin $(XWLANG)%_newheader.bin charcount.bin table.bin values.bin frankspecials.bin
+ cat $(XWLANG)$*_flags.bin $(XWLANG)$*_newheader.bin charcount.bin table.bin values.bin \
frankspecials.bin $(XWLANG)StartLoc.bin \
$$(ls dawg$(XWLANG)$*_*.bin) > $@
cp $@ saveme.bin
@@ -254,7 +257,7 @@ dawg$(XWLANG)%.stamp: $(XWLANG)Main.dict.gz $(DICT2DAWG) table.bin ../Makefile.l
start=$$(echo $@ | sed -e 's/dawg$(XWLANG)\([0-9]*\)to[0-9]*.stamp/\1/'); \
end=$$(echo $@ | sed -e 's/dawg$(XWLANG)[0-9]*to\([0-9]*\).stamp/\1/'); \
echo $${start} and $${end}; \
- zcat $< | $(BOWDLERIZER) | $(DICT2DAWG) $(DICT2DAWGARGS) $(TABLE_ARG) table.bin -b 28000 \
+ zcat $< | $(BOWDLERIZER) | $(DICT2DAWG) $(DICT2DAWGARGS) $(TABLE_ARG) table.bin \
-ob dawg$(XWLANG)$* $(ENCP) \
-sn $(XWLANG)StartLoc.bin -min $${start} -max $${end} \
-wc $(XWLANG)$*_wordcount.bin $(FORCE_4) -ns $(XWLANG)$*_nodesize.bin
@@ -269,24 +272,24 @@ allbins:
$(MAKE) TARGET_TYPE=FRANK byodbins
rm palmspecials.bin
-table.bin: ../xloc.pl
+table.bin: ../xloc.py
ifdef NEWDAWG
- perl -I../ ../xloc.pl $(ENCP) -tn -out $@
+ ../xloc.py $(ENCP) -tn -out $@
else
- perl -I../ ../xloc.pl -t -out $@
+ error
endif
-values.bin: ../xloc.pl
- perl -I../ ../xloc.pl -v -out $@
+values.bin: ../xloc.py
+ ../xloc.py -v -out $@
# a binary file, two bytes, one giving the size of tiles data and the
# other the number of tiles in the dict. Tiles data is utf-8 and so
# number is not derivable from size.
-$(XWLANG)_charcount.bin: table.bin ../xloc.pl
+charcount.bin: table.bin ../xloc.py
SIZ=$$(ls -l $< | awk '{print $$5}'); \
perl -e "print pack(\"c\",$$SIZ)" > $@
TMP=/tmp/tmp$$$$; \
- perl -I../ ../xloc.pl -s -out $$TMP; \
+ ../xloc.py -s -out $$TMP; \
cat $$TMP >> $@; \
rm -f $$TMP
diff --git a/xwords4/dawg/allchars.pl b/xwords4/dawg/allchars.pl
deleted file mode 100755
index 361376ef8..000000000
--- a/xwords4/dawg/allchars.pl
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/usr/bin/perl
-
-# Print all ascii characters (for pasting into Makefiles etc. when you
-# don't know what key combination produces them)
-
-use strict;
-
-for ( my $i = int(' '); $i <= 255; ++$i ) {
- printf "%.3d: %c\n", $i, $i;
-}
diff --git a/xwords4/dawg/dawg2dict.py b/xwords4/dawg/dawg2dict.py
index 460ca8c33..6a4da909d 100755
--- a/xwords4/dawg/dawg2dict.py
+++ b/xwords4/dawg/dawg2dict.py
@@ -66,7 +66,8 @@ def eatBitmap( fh ):
if nCols > 0:
nRows = int(oneByteFmt.unpack(fh.read(oneByteFmt.size))[0])
nBytes = ((nRows*nCols)+7) // 8
- print('eatBitmap(): skipping', nBytes, 'bytes; nCols:', nCols, 'nRows:', nRows)
+ print('eatBitmap(): skipping {} bytes; nCols: {}, nRows: {}:'.format(nBytes,nCols, nRows),\
+ file=sys.stderr)
fh.read(nBytes)
def loadSpecialData( fh, data ):
@@ -187,8 +188,7 @@ def process(args):
numFaces = int(oneByteFmt.unpack(dawg.read(oneByteFmt.size))[0])
if not isUTF8:
numFaceBytes = numFaces * 2
- if numFaces > 64:
- error("too many faces: " + numFaces)
+ assert numFaces <= 64, 'too many faces: {}'.format(numFaces)
print( 'numFaceBytes: {}, numFaces: {}'.format(numFaceBytes, numFaces), file=sys.stderr )
print( 'TODO: confirm checksum', file=sys.stderr )
@@ -227,7 +227,9 @@ def process(args):
# assert len(words) == nWords
if args.DUMP_WORDS:
for word in words:
- print(word)
+ # if we're piped to head we'll get an exception, so just exit
+ try: print(word)
+ except: break
def mkParser():
parser = argparse.ArgumentParser()
diff --git a/xwords4/dawg/dict2dawg.pl b/xwords4/dawg/dict2dawg.pl
deleted file mode 100755
index 064dff84e..000000000
--- a/xwords4/dawg/dict2dawg.pl
+++ /dev/null
@@ -1,857 +0,0 @@
-#!/usr/bin/perl
-
-##############################################################################
-# adapted from C++ code Copyright (C) 2000 Falk Hueffner
-# This version Copyright (C) 2002 Eric House (xwords@eehouse.org)
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-# USA
-##############################################################################
-
-# inputs: 0. Name of file mapping letters to 0..31 values. In English
-# case just contains A..Z. This will be used to translate the tries
-# on output.
-# 1. Max number of bytes per binary output file.
-#
-# 2. Basename of binary files for output.
-
-# 3. Name of file to which to write the number of the
-# startNode, since I'm not rewriting a bunch of code to expect Falk's
-# '*' node at the start.
-#
-
-# In STDIN, the text file to be compressed. It absolutely
-# must be sorted. The sort doesn't have to follow the order in the
-# map file, however.
-
-# This is meant eventually to be runnable as part of a cgi system for
-# letting users generate Crosswords dicts online.
-
-
-
-use strict;
-use POSIX;
-
-my $gFirstDiff;
-my @gCurrentWord;
-my $gCurrentWord; # save so can check for sortedness
-my $gDone = 0;
-my @gInputStrings;
-my $gNeedsSort = 1; # read from cmd line eventually
-my @gNodes; # final array of nodes
-my $gNBytesPerOutfile = 0xFFFFFFFF;
-my $gTableFile;
-my $gOutFileBase;
-my $gStartNodeOut;
-my $gInFileName;
-my $gKillIfMissing = 1;
-my $gTermChar = '/n';
-my $gDumpText = 0; # dump the dict as text after?
-my $gCountFile;
-my $gBytesPerNodeFile; # where to write whether node size 3 or 4
-my $gWordCount = 0;
-my %gTableHash;
-my $gBlankIndex;
-my @gRevMap;
-my $debug = 0;
-my %gSubsHash;
-my $gForceFour = 0; # use four bytes regardless of need?
-my $gNBytesPerNode;
-my $gUseUnicode;
-
-main();
-
-exit;
-
-##############################################################################
-
-sub main() {
-
- if ( !parseARGV() ) {
- usage();
- exit();
- }
-
- makeTableHash();
-
- my $infile;
-
- if ( $gInFileName ) {
- open $infile, "<$gInFileName";
- } else {
- $infile = \*STDIN;
- }
-
- @gInputStrings = parseAndSort( $infile );
- if ( $gInFileName ) {
- close $infile;
- }
-
- # Do I need this stupid thing? Better to move the first row to
- # the front of the array and patch everything else. Or fix the
- # non-palm dictionary format to include the offset of the first
- # node.
-
- my $dummyNode = 0xFFFFFFFF;
- @gNodes = ( $dummyNode );
-
- readNextWord();
-
- my $firstRootChildOffset = buildNode(0);
-
- moveTopToFront( \$firstRootChildOffset );
-
- if ( $gStartNodeOut ) {
- writeOutStartNode( $gStartNodeOut, $firstRootChildOffset );
- }
-
- print STDERR "\n... dumping table ...\n" if $debug;
- printNodes( \@gNodes, "done with main" ) if $debug;
-
- # write out the number of nodes if requested
- if ( $gCountFile ) {
- open OFILE, "> $gCountFile";
- print OFILE pack( "N", $gWordCount );
- close OFILE;
- print STDERR "wrote out: got $gWordCount words\n";
- }
-
- if ( $gOutFileBase ) {
- emitNodes( $gNBytesPerOutfile, $gOutFileBase );
- }
-
- if ( $gDumpText && @gNodes > 0 ) {
- printOneLevel( $firstRootChildOffset, "" );
- }
-
- if ( $gBytesPerNodeFile ) {
- open OFILE, "> $gBytesPerNodeFile";
- print OFILE $gNBytesPerNode;
- close OFILE;
- }
- print STDERR "Used $gNBytesPerNode per node.\n";
-} # main
-
-# We now have an array of nodes with the last subarray being the
-# logical top of the tree. Move them to the start, fixing all fco
-# refs, so that legacy code like Palm can assume top==0.
-#
-# Note: It'd probably be a bit faster to integrate this with emitNodes
-# -- unless I need to have an in-memory list that can be used for
-# lookups. But that's best for debugging, so keep it this way for now.
-#
-# Also Note: the first node is a dummy that can and should be tossed
-# now.
-
-sub moveTopToFront($) {
- my ( $firstRef ) = @_;
-
- my $firstChild = ${$firstRef};
- ${$firstRef} = 0;
- my @lastSub;
-
- if ( $firstChild > 0 ) {
- # remove the last (the root) subarray
- @lastSub = splice( @gNodes, $firstChild );
- } else {
- die "there should be no words!!" if $gWordCount != 0;
- }
- # remove the first (garbage) node
- shift @gNodes;
-
- my $diff;
- if ( $firstChild > 0 ) {
- # -1 because all move down by 1; see prev line
- $diff = @lastSub - 1;
- die "something wrong with len\n" if $diff < 0;
- } else {
- $diff = 0;
- }
-
- # stick it on the front
- splice( @gNodes, 0, 0, @lastSub);
-
- # We add $diff to everything. There's no subtracting because
- # nobody had any refs to the top list.
-
- for ( my $i = 0; $i < @gNodes; ++$i ) {
- my $fco = TrieNodeGetFirstChildOffset( $gNodes[$i] );
- if ( $fco != 0 ) { # 0 means NONE, not 0th!!
- TrieNodeSetFirstChildOffset( \$gNodes[$i], $fco+$diff );
- }
- }
-} # moveTopToFront
-
-
-sub buildNode {
- my ( $depth ) = @_;
-
- if ( @gCurrentWord == $depth ) {
- # End of word reached. If the next word isn't a continuation
- # of the current one, then we've reached the bottom of the
- # recursion tree.
- readNextWord();
- if ($gFirstDiff < $depth || $gDone) {
- return 0;
- }
- }
-
- my @newedges;
-
- do {
- my $letter = $gCurrentWord[$depth];
- my $isTerminal = @gCurrentWord - 1 == $depth ? 1:0;
-
- my $nodeOffset = buildNode($depth+1);
- my $newNode = MakeTrieNode($letter, $isTerminal, $nodeOffset);
- push( @newedges, $newNode );
-
- } while ( ($gFirstDiff == $depth) && !$gDone);
-
- TrieNodeSetIsLastSibling( \@newedges[@newedges-1], 1 );
-
- return addNodes( \@newedges );
-} # buildNode
-
-sub addNodes {
- my ( $newedgesR ) = @_;
-
- my $found = findSubArray( $newedgesR );
-
- if ( $found >= 0 ) {
- die "0 is an invalid match!!!" if $found == 0;
- return $found;
- } else {
-
- my $firstFreeIndex = @gNodes;
-
- print STDERR "adding...\n" if $debug;
- printNodes( $newedgesR ) if $debug;
-
- push @gNodes, (@{$newedgesR});
-
- registerSubArray( $newedgesR, $firstFreeIndex );
- return $firstFreeIndex;
- }
-} # addNodes
-
-sub printNode {
- my ( $index, $node ) = @_;
-
- print STDERR "[$index] ";
-
- my $letter = TrieNodeGetLetter($node);
- printf( STDERR
- "letter=%d(%s); isTerminal=%d; isLastSib=%d; fco=%d;\n",
- $letter, "" . $gRevMap[$letter],
- TrieNodeGetIsTerminal($node),
- TrieNodeGetIsLastSibling($node),
- TrieNodeGetFirstChildOffset($node));
-} # printNode
-
-sub printNodes {
- my ( $nodesR, $name ) = @_;
-
- my $len = @{$nodesR};
- # print "printNodes($name): len = $len\n";
-
- for ( my $i = 0; $i < $len; ++$i ) {
- my $node = ${$nodesR}[$i];
- printNode( $i, $node );
- }
-
-}
-
-
-# Hashing. We'll keep a hash of offsets into the existing nodes
-# array, and as the key use a string that represents the entire sub
-# array. Since the key is what we're matching for, there should never
-# be more than one value per hash and so we don't need buckets.
-# Return -1 if there's no match.
-
-sub findSubArray {
- my ( $newedgesR ) = @_;
-
- my $key = join('', @{$newedgesR});
-
- if ( exists( $gSubsHash{$key} ) ) {
- return $gSubsHash{$key};
- } else {
- return -1;
- }
-} # findSubArray
-
-# add to the hash
-sub registerSubArray {
- my ( $edgesR, $nodeLoc ) = @_;
-
- my $key = join( '', @{$edgesR} );
-
- if ( exists $gSubsHash{$key} ) {
- die "entry for key shouldn't exist!!";
- } else {
- $gSubsHash{$key} = $nodeLoc;
- }
-
-} # registerSubArray
-
-sub toWord($) {
- my ( $tileARef ) = @_;
- my $word = "";
-
- foreach my $tile (@$tileARef) {
- foreach my $letter (keys (%gTableHash) ) {
- if ( $tile == $gTableHash{$letter} ) {
- $word .= $letter;
- last;
- }
- }
- }
-
- return $word;
-}
-
-sub readNextWord() {
- my @word;
-
- if ( !$gDone ) {
- $gDone = @gInputStrings == 0;
- if ( !$gDone ) {
- @word = @{shift @gInputStrings};
- } else {
- print STDERR "gDone set to true\n" if $debug;
- }
-
- print STDERR "got word: ", join(',',@word), "\n" if $debug;
- }
- my $numCommonLetters = 0;
- my $len = @word;
- if ( @gCurrentWord < $len ) {
- $len = @gCurrentWord;
- }
-
- while ( @gCurrentWord[$numCommonLetters] eq @word[$numCommonLetters]
- && $numCommonLetters < $len) {
- ++$numCommonLetters;
- }
-
- $gFirstDiff = $numCommonLetters;
- if ( #$debug &&
- @gCurrentWord > 0 && @word > 0
- && !firstBeforeSecond( \@gCurrentWord, \@word ) ) {
- die "words ", join(",",@gCurrentWord), " (" . toWord(\@gCurrentWord) .
- ") and " . join(",", @word) . " (" . toWord(\@word) .
- ") out of order";
- }
- @gCurrentWord = @word;
-} # readNextWord
-
-sub firstBeforeSecond {
- my ( $firstR, $secondR ) = @_;
-
- for ( my $i = 0; ; ++$i ) {
-
- # if we reach the end of the first word/list, we're done.
- if ( $i == @{$firstR} ) {
- die "duplicate!!!" if $i == @{$secondR};
- return 1;
- # but if we reach the second end first, we've failed
- } elsif ( $i == @{$secondR} ) {
- return 0;
- }
-
- my $diff = ${$firstR}[$i] <=> ${$secondR}[$i];
-
- if ( $diff == 0 ) {
- next;
- } else {
- return $diff < 0;
- }
- }
-} # firstBeforeSecond
-
-# passed to sort. Should remain unprototyped for effeciency's sake
-
-sub cmpWords {
-
- my $lenA = @{$a};
- my $lenB = @{$b};
- my $min = $lenA > $lenB? $lenB: $lenA;
-
- for ( my $i = 0; $i < $min; ++$i ) {
- my $ac = ${$a}[$i];
- my $bc = ${$b}[$i];
-
- my $res = $ac <=> $bc;
-
- if ( $res != 0 ) {
- return $res; # we're done
- }
- }
-
- # If we got here, they match up to their common length. Longer is
- # greater.
- my $res = @{$a} <=> @{$b};
- return $res; # which is longer?
-} # cmpWords
-
-sub parseAndSort() {
- my ( $infile ) = @_;
-
- my @wordlist;
- my @word;
-
- my $lastWord;
- WORDLOOP:
- for ( ; ; ) {
-
- my $dropWord = 0;
- splice @word; # empty it
-
- # for each byte
- for ( ; ; ) {
- my $byt = getc($infile);
-
- if ( $byt eq undef ) {
- last WORDLOOP;
- } elsif ( $byt eq $gTermChar ) {
- if ( !$dropWord ) {
- push @wordlist, [ @word ];
- ++$gWordCount;
- }
- $lastWord = "";
- next WORDLOOP;
- } elsif ( exists( $gTableHash{$byt} ) ) {
- if ( !$dropWord ) {
- push @word, $gTableHash{$byt};
- die "word too long" if @word > 15;
- if ( $gKillIfMissing ) {
- $lastWord .= $byt;
- }
- }
- } elsif ($gKillIfMissing) {
- die "$0: chr $byt (", $byt+0, ") not in map file $gTableFile\n"
- . "last word was $lastWord\n";
- } else {
- $dropWord = 1;
- splice @word; # lose anything we already have
- }
- }
- }
-
- if ( $gNeedsSort && ($gWordCount > 0) ) {
- print STDERR "starting sort...\n" if $debug;
- @wordlist = sort cmpWords @wordlist;
- print STDERR "sort finished\n" if $debug;
- }
-
- print STDERR "length of list is ", @wordlist + 0, ".\n" if $debug;
-
- return @wordlist;
-} # parseAndSort
-
-# Print binary representation of trie array. This isn't used yet, but
-# eventually it'll want to dump to multiple files appropriate for Palm
-# that can be catenated together on other platforms. There'll need to
-# be a file giving the offset of the first node too. Also, might want
-# to move to 4-byte representation when the input can't otherwise be
-# handled.
-
-sub dumpNodes {
-
- for ( my $i = 0; $i < @gNodes; ++$i ) {
- my $node = $gNodes[$i];
- my $bstr = pack( "I", $node );
- print STDOUT $bstr;
- }
-}
-
-##############################################################################
-# Little node-field setters and getters to hide what bits represent
-# what.
-#
-# high bit (31) is ACCEPTING bit
-# next bit (30) is LAST_SIBLING bit
-# next 6 bits (29-24) are tile bit (allowing alphabets of 64 letters)
-# final 24 bits (23-0) are the index of the first child (fco)
-#
-##############################################################################
-
-sub TrieNodeSetIsTerminal {
- my ( $nodeR, $isTerminal ) = @_;
-
- if ( $isTerminal ) {
- ${$nodeR} |= (1 << 31);
- } else {
- ${$nodeR} &= ~(1 << 31);
- }
-}
-
-sub TrieNodeGetIsTerminal {
- my ( $node ) = @_;
- return ($node & (1 << 31)) != 0;
-}
-
-sub TrieNodeSetIsLastSibling {
- my ( $nodeR, $isLastSibling ) = @_;
- if ( $isLastSibling ) {
- ${$nodeR} |= (1 << 30);
- } else {
- ${$nodeR} &= ~(1 << 30);
- }
-}
-
-sub TrieNodeGetIsLastSibling {
- my ( $node ) = @_;
- return ($node & (1 << 30)) != 0;
-}
-
-sub TrieNodeSetLetter {
- my ( $nodeR, $letter ) = @_;
-
- die "$0: letter ", $letter, " too big" if $letter >= 64;
-
- my $mask = ~(0x3F << 24);
- ${$nodeR} &= $mask; # clear all the bits
- ${$nodeR} |= ($letter << 24); # set new ones
-}
-
-sub TrieNodeGetLetter {
- my ( $node ) = @_;
- $node >>= 24;
- $node &= 0x3F; # is 3f ok for 3-byte case???
- return $node;
-}
-
-sub TrieNodeSetFirstChildOffset {
- my ( $nodeR, $fco ) = @_;
-
- die "$0: $fco larger than 24 bits" if ($fco & 0xFF000000) != 0;
-
- my $mask = ~0x00FFFFFF;
- ${$nodeR} &= $mask; # clear all the bits
- ${$nodeR} |= $fco; # set new ones
-}
-
-sub TrieNodeGetFirstChildOffset {
- my ( $node ) = @_;
- $node &= 0x00FFFFFF; # 24 bits
- return $node;
-}
-
-
-sub MakeTrieNode {
- my ( $letter, $isTerminal, $firstChildOffset, $isLastSibling ) = @_;
- my $result = 0;
-
- TrieNodeSetIsTerminal( \$result, $isTerminal );
- TrieNodeSetIsLastSibling( \$result, $isLastSibling );
- TrieNodeSetLetter( \$result, $letter );
- TrieNodeSetFirstChildOffset( \$result, $firstChildOffset );
-
- return $result;
-} # MakeTrieNode
-
-# Caller may need to know the offset of the first top-level node.
-# Write it here.
-sub writeOutStartNode {
- my ( $startNodeOut, $firstRootChildOffset ) = @_;
-
- open NODEOUT, ">$startNodeOut";
- print NODEOUT pack( "N", $firstRootChildOffset );
- close NODEOUT;
-} # writeOutStartNode
-
-# build the hash for translating. I'm using a hash assuming it'll be
-# fast. Key is the letter; value is the 0..31 value to be output.
-sub makeTableHash {
- my $i;
- open TABLEFILE, "< $gTableFile";
-
- splice @gRevMap; # empty it
-
- for ( $i = 0; ; ++$i ) {
- my $ch = getc(TABLEFILE);
- if ( $ch eq undef ) {
- last;
- }
-
- if ( $gUseUnicode ) { # skip the first byte each time: tmp HACK!!!
- $ch = getc(TABLEFILE);
- }
- if ( $ch eq undef ) {
- last;
- }
-
- push @gRevMap, $ch;
-
- if ( ord($ch) == 0 ) { # blank
- $gBlankIndex = $i;
- next; # we want to increment i when blank seen since
- # it is a tile value
- }
-
- die "$0: $gTableFile too large\n" if $i > 64;
- die "$0: only blank (0) can be 64th char\n" if ($i == 64 && $ch != 0);
-
- $gTableHash{$ch} = $i;
- }
-
- close TABLEFILE;
-} # makeTableHash
-
-# emitNodes. "input" is $gNodes. From it we write up to
-# $nBytesPerOutfile to files named $outFileBase0..n, mapping the
-# letter field down to 5 bits with a hash built from $tableFile. If
-# at any point we encounter a letter not in the hash we fail with an
-# error.
-
-sub emitNodes($$) {
- my ( $nBytesPerOutfile, $outFileBase ) = @_;
-
- # now do the emit.
-
- # is 17 bits enough?
- printf STDERR ("There are %d (0x%x) nodes in this DAWG.\n",
- 0 + @gNodes, 0 + @gNodes );
- my $nTiles = 0 + keys(%gTableHash); # blank is not included in this count!
- if ( @gNodes > 0x1FFFF || $gForceFour || $nTiles > 32 ) {
- $gNBytesPerNode = 4;
- } elsif ( $nTiles < 32 ) {
- $gNBytesPerNode = 3;
- } else {
- if ( $gBlankIndex == 32 ) { # blank
- print STDERR "blank's at 32; 3-byte-nodes still ok\n";
- $gNBytesPerNode = 3;
- } else {
- die "$0: move blank to last position in info.txt for smaller DAWG";
- }
- }
-
- my $nextIndex = 0;
- my $nextFileNum = 0;
-
- for ( $nextFileNum = 0; ; ++$nextFileNum ) {
-
- if ( $nextIndex >= @gNodes ) {
- last; # we're done
- }
-
- die "Too many outfiles; infinite loop?" if $nextFileNum > 99;
-
- my $outName = sprintf("${outFileBase}_%03d.bin", $nextFileNum);
- open OUTFILE, "> $outName";
- binmode( OUTFILE );
- my $curSize = 0;
-
- while ( $nextIndex < @gNodes ) {
-
- # scan to find the next terminal
- my $i;
- for ( $i = $nextIndex;
- !TrieNodeGetIsLastSibling($gNodes[$i]);
- ++$i ) {
-
- # do nothing but a sanity check
- if ( $i >= @gNodes) {
- die "bad trie format: last node not last sibling" ;
- }
-
- }
- ++$i; # move beyond the terminal
- my $nextSize = ($i - $nextIndex) * $gNBytesPerNode;
- if ($curSize + $nextSize > $nBytesPerOutfile) {
- last;
- } else {
- # emit the subarray
- while ( $nextIndex < $i ) {
- outputNode( $gNodes[$nextIndex], $gNBytesPerNode,
- \*OUTFILE );
- ++$nextIndex;
- }
- $curSize += $nextSize;
- }
- }
-
- close OUTFILE;
- }
-
-} # emitNodes
-
-sub printWord {
- my ( $str ) = @_;
-
- print STDERR "$str\n";
-}
-
-# print out the entire dictionary, as text, to STDERR.
-
-sub printOneLevel {
-
- my ( $index, $str ) = @_;
-
- for ( ; ; ) {
-
- my $newStr = $str;
- my $node = $gNodes[$index++];
-
- my $lindx = $gRevMap[TrieNodeGetLetter($node)];
-
- if ( ord($lindx) >= 0x20 ) {
- $newStr .= "$lindx";
- } else {
- print STDERR "sub space" if $debug;
- $newStr .= "\\" . chr('0'+$lindx);
- }
-
- if ( TrieNodeGetIsTerminal($node) ) {
- printWord( $newStr );
- }
-
- my $fco = TrieNodeGetFirstChildOffset( $node );
- if ( $fco != 0 ) {
- printOneLevel( $fco, $newStr );
- }
-
- if ( TrieNodeGetIsLastSibling($node) ) {
- last;
- }
- }
-}
-
-sub outputNode ($$$) {
- my ( $node, $nBytes, $outfile ) = @_;
-
- my $fco = TrieNodeGetFirstChildOffset($node);
- my $fourthByte;
-
- if ( $nBytes == 4 ) {
- $fourthByte = $fco >> 16;
- die "$0: fco too big" if $fourthByte > 0xFF;
- $fco &= 0xFFFF;
- }
-
- # Formats are different depending on whether it's to have 3- or
- # 4-byte nodes.
-
- # Here's what the three-byte node looks like. 16 bits plus one
- # burried in the last byte for the next node address, five for a
- # character/tile and one each for accepting and last-edge.
-
- # 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
- # |-------- 16 bits of next node address -------| | | | |-tile indx-|
- # | | |
- # accepting bit ---+ | |
- # last edge bit ------+ |
- # ---- last bit (17th on next node addr)---------+
-
- # The four-byte format adds a byte at the right end for
- # addressing, but removes the extra bit (5) in order to let the
- # chars field be six bits. Bits 7 and 6 remain the same.
-
- # write the fco (less that one bit). We want two bytes worth
- # in three-byte mode, and three in four-byte mode
-
- # first two bytes are low-word of fco, regardless of format
- for ( my $i = 1; $i >= 0; --$i ) {
- my $tmp = ($fco >> ($i * 8)) & 0xFF;
- print $outfile pack( "C", $tmp );
- }
- $fco >>= 16; # it should now be 1 or 0
- die "fco not 1 or 0" if $fco > 1;
-
- my $chIn5 = TrieNodeGetLetter($node);
- my $bits = $chIn5;
- die "$0: char $bits too big" if $bits > 0x1F && $nBytes == 3;
-
- if ( TrieNodeGetIsLastSibling($node) ) {
- $bits |= 0x40;
- }
- if ( TrieNodeGetIsTerminal($node) ) {
- $bits |= 0x80;
- }
-
- # We set the 17th next-node bit only in 3-byte case (where char is
- # 5 bits)
- if ( $nBytes == 3 && $fco != 0 ) {
- $bits |= 0x20;
- }
- print $outfile pack( "C", $bits );
-
- # the final byte, if in use
- if ( $nBytes == 4 ) {
- print $outfile pack( "C", $fourthByte );
- }
-} # outputNode
-
-sub usage {
- print STDERR "usage: $0 \n"
- . "\t[-b bytesPerFile] (default = 0xFFFFFFFF)\n"
- . "\t-m mapFile\n"
- . "\t-mn mapFile (unicode)\n"
- . "\t-ob outFileBase\n"
- . "\t-sn start node out file\n"
- . "\t[-if input file name] -- default = stdin\n"
- . "\t[-term ch] (word terminator -- default = '\\0'\n"
- . "\t[-nosort] (input already sorted in accord with -m; " .
- " default=sort'\n"
- . "\t[-dump] (write dictionary as text to STDERR for testing)\n"
- . "\t[-force4](use 4 bytes per node regardless of need)\n"
- . "\t[-r] (reject words with letters not in mapfile)\n"
- . "\t[-k] (kill if any letters no in mapfile -- default)\n"
- . "\t[-debug] (print a bunch of stuff)\n"
- ;
-
-} # usage
-
-sub parseARGV {
-
- my $arg;
- while ( my $arg = shift(@ARGV) ) {
-
- SWITCH: {
- if ($arg =~ /-b/) {$gNBytesPerOutfile = shift(@ARGV), last SWITCH;}
- if ($arg =~ /-mn/) {$gTableFile = shift(@ARGV);
- $gUseUnicode = 1;
- last SWITCH;}
- if ($arg =~ /-m/) {$gTableFile = shift(@ARGV); last SWITCH;}
- if ($arg =~ /-ob/) {$gOutFileBase = shift(@ARGV), last SWITCH;}
- if ($arg =~ /-sn/) {$gStartNodeOut = shift(@ARGV), last SWITCH;}
- if ($arg =~ /-if/) {$gInFileName = shift(@ARGV), last SWITCH;}
- if ($arg =~ /-r/) {$gKillIfMissing = 0; last SWITCH;}
- if ($arg =~ /-k/) {$gKillIfMissing = 1; last SWITCH;}
- if ($arg =~ /-term/) {$gTermChar = chr(shift(@ARGV)); last SWITCH;}
- if ($arg =~ /-dump/) {$gDumpText = 1; last SWITCH;}
- if ($arg =~ /-nosort/) {$gNeedsSort = 0; last SWITCH;}
- if ($arg =~ /-wc/) {$gCountFile = shift(@ARGV); last SWITCH;}
- if ($arg =~ /-ns/) {$gBytesPerNodeFile = shift(@ARGV); last SWITCH;}
- if ($arg =~ /-force4/) {$gForceFour = 1; last SWITCH;}
- # accept -fsize for compatibility with c++ version (but drop it)
- if ($arg =~ /-fsize/) {shift(@ARGV); last SWITCH;}
- if ($arg =~ /-debug/) {$debug = 1; last SWITCH;}
- die "unexpected arg $arg\n";
- }
- }
-
-
- print STDERR "gNBytesPerOutfile=$gNBytesPerOutfile\n" if $debug;
- print STDERR "gTableFile=$gTableFile\n" if $debug;
- print STDERR "gOutFileBase=$gOutFileBase\n" if $debug;
- print STDERR "gStartNodeOut=$gStartNodeOut\n" if $debug;
- printf STDERR "gTermChar=%s(%d)\n", $gTermChar, ord($gTermChar) if $debug;
-
- return $gTableFile;
-
-} # parseARGV
diff --git a/xwords4/dawg/dictstats.pl b/xwords4/dawg/dictstats.pl
deleted file mode 100755
index 819b82e35..000000000
--- a/xwords4/dawg/dictstats.pl
+++ /dev/null
@@ -1,76 +0,0 @@
-#!/usr/bin/perl
-
-# print stats about in input stream that's assumed to be a dictionary.
-# Counts and percentages of each letter, as well as total numbers of
-# words. This is not part of the dictionary build process. I use it
-# for creating info.txt files for new languages and debugging the
-# creation of dictionaries from new wordlists.
-#
-# Something like this might form the basis for choosing counts and
-# values for tiles without using the conventions established by
-# Scrabble players. This isn't enough, though: the frequency of
-# letter tuples and triples -- how often letters appear together -- is
-# a better indicator than just letter count.
-
-use strict;
-
-my @wordSizeCounts;
-my %letterCounts;
-my $wordCount;
-my $letterCount;
-my $enc = "utf8"; # this could be a cmdline arg....
-
-if ( $enc ) {
- binmode( STDOUT, ":encoding($enc)" ) ;
- binmode( STDIN, ":encoding($enc)" ) ;
-}
-
-while (<>) {
-
- chomp;
-
- ++$wordSizeCounts[length];
- ++$wordCount;
-
- foreach my $letter (split( / */ ) ) {
- my $ii = ord($letter);
- # special-case the bogus chars we add for "specials"
- die "$0: this is a letter?: $ii" if $ii <= 32 && $ii >= 4 && $ii != 0;
- ++$letterCounts{$letter};
- ++$letterCount;
- }
-}
-
-print "Number of words: $wordCount\n";
-print "Number of letters: $letterCount\n\n";
-
-
-print "**** word sizes ****\n";
-print "SIZE COUNT PERCENT\n";
-my $pctTotal = 0.0;
-my $wordTotal = 0;
-for ( my $i = 1 ; $i <= 99; ++$i ) {
- my $count = $wordSizeCounts[$i];
- $wordTotal += $count;
- if ( $count > 0 ) {
- my $pct = (100.00 * $count)/$wordCount;
- $pctTotal += $pct;
- printf "%2d %6d %.2f\n", $i, $count, $pct;
- }
-}
-printf "-------------------------------\n";
-printf " %6d %.2f\n", $wordTotal, $pctTotal;
-
-
-print "\n\n**** Letter counts ****\n";
-print " ASCII ORD HEX PCT (of $letterCount)\n";
-my $lineNo = 1;
-foreach my $key (sort keys %letterCounts) {
- my $count = $letterCounts{$key};
- my $pct = (100.00 * $count) / $letterCount;
- printf( "%2d: %3s %3d %x %5.2f (%d)\n",
- $lineNo, $key, ord($key), ord($key), $pct, $count );
- ++$lineNo;
-}
-
-print "\n";
diff --git a/xwords4/dawg/dictstats.py b/xwords4/dawg/dictstats.py
new file mode 100755
index 000000000..28bfb631f
--- /dev/null
+++ b/xwords4/dawg/dictstats.py
@@ -0,0 +1,80 @@
+#!/usr/bin/env python3
+
+import sys
+
+"""
+print stats about in input stream that's assumed to be a dictionary.
+Counts and percentages of each letter, as well as total numbers of
+words. This is not part of the dictionary build process. I use it
+for creating info.txt files for new languages and debugging the
+creation of dictionaries from new wordlists.
+
+Something like this might form the basis for choosing counts and
+values for tiles without using the conventions established by
+Scrabble players. This isn't enough, though: the frequency of
+letter tuples and triples -- how often letters appear together -- is
+a better indicator than just letter count.
+"""
+
+
+
+def main():
+ wordSizeCounts = {}
+ letterCounts = {}
+ wordCount = 0
+ letterCount = 0
+ enc = 'utf8' # this could be a cmdline arg....
+
+ for line in sys.stdin.readlines():
+ line = line.strip()
+
+ length = len(line)
+ if not length in wordSizeCounts: wordSizeCounts[length] = 0
+ wordSizeCounts[length] += 1
+ wordCount += 1
+
+ for letter in line:
+ ii = ord(letter)
+ # perl did this: die "$0: this is a letter?: $ii" if $ii <= 32 && $ii >= 4 && $ii != 0;
+ assert ii > 32 or ii < 4 or ii == 0, 'letter {} out of range'.format(ii)
+ if not letter in letterCounts: letterCounts[letter] = 0
+ letterCounts[letter] += 1
+ letterCount += 1
+
+ print( 'Number of words: {}'.format(wordCount))
+ print( 'Number of letters: {}'.format(letterCount))
+ print('')
+
+ print( '**** word sizes ****' )
+ print( 'SIZE COUNT PERCENT' )
+ pctTotal = 0.0
+ wordTotal = 0
+ for ii in sorted(wordSizeCounts):
+ count = wordSizeCounts[ii]
+ wordTotal += count
+ pct = (100.00 * count)/wordCount
+ pctTotal += pct
+ print( '{:2d} {:6d} {:02.2f}'.format(ii, count, pct))
+
+ print( '-------------------------------' )
+ print(' {:6d} {:.2f}'.format( wordTotal, pctTotal))
+ print('')
+
+ lineNo = 1
+ pctTotal = 0.0
+ print( '**** Letter counts ****' )
+ print( ' ASCII ORD HEX PCT (of {})'.format(letterCount))
+ for letter in sorted(letterCounts):
+ count = letterCounts[letter]
+ pct = (100.00 * count) / letterCount
+ pctTotal += pct
+ print( '{:2d}: {: >6s} {:2d} {:x} {:5.2f} ({:d})' \
+ .format(lineNo, letter, ord(letter), ord(letter), pct, count ) )
+ lineNo += 1
+
+ print('percent total {:.2f}'.format( pctTotal))
+ print('')
+
+##############################################################################
+if __name__ == '__main__':
+ main()
diff --git a/xwords4/dawg/frank_mkspecials.pl b/xwords4/dawg/frank_mkspecials.pl
deleted file mode 100755
index 5c0ed4465..000000000
--- a/xwords4/dawg/frank_mkspecials.pl
+++ /dev/null
@@ -1,47 +0,0 @@
-#!/usr/bin/perl
-
-# Copyright 2001 by Eric House (xwords@eehouse.org)
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-# Given arguments consisting of triples, first a string and then pbitm
-# files representing bitmaps. For each triple, print out the string and
-# then the converted bitmaps.
-
-use strict;
-
-while ( @ARGV ) {
- my $str = shift();
- my $largebmp = shift();
- my $smallbmp = shift();
-
- doOne( $str, $largebmp, $smallbmp );
-}
-
-sub doOne {
- my ( $str, $largebmp, $smallbmp ) = @_;
-
- print pack( "C", length($str) );
- print $str;
-
- print STDERR "looking at $largebmp", "\n";
-
- die "file $largebmp does not exist\n" unless -e $largebmp;
- print `cat $largebmp | ../pbitm2bin.pl`;
- die "file $smallbmp does not exist\n" unless -e $smallbmp;
- print `cat $smallbmp | ../pbitm2bin.pl`;
-}
-
-
diff --git a/xwords4/dawg/frank_mkspecials.py b/xwords4/dawg/frank_mkspecials.py
new file mode 100755
index 000000000..0d69af876
--- /dev/null
+++ b/xwords4/dawg/frank_mkspecials.py
@@ -0,0 +1,49 @@
+#!/usr/bin/env python3
+
+# Copyright 2001 - 2020 by Eric House (xwords@eehouse.org)
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+# Given arguments consisting of triples, first a string and then pbitm
+# files representing bitmaps. For each triple, print out the string and
+# then the converted bitmaps.
+
+import os, subprocess, struct, sys
+
+def doFile(bmp):
+ assert os.path.exists(bmp), 'no such file: {}'.format(bmp)
+ with open(bmp, 'r') as file:
+ subprocess.run(['../pbitm2bin.py'], stdin=file)
+
+def doOne( face, largebmp, smallbmp ):
+ # print('doOne({}, {}, {})'.format(face, largebmp, smallbmp), file=sys.stderr)
+ with os.fdopen(sys.stdout.fileno(), 'wb', closefd=False) as stdout:
+ encoded = face.encode('utf-8')
+ stdout.write(struct.pack('B', len(encoded)))
+ stdout.write(encoded)
+
+ for fil in [ largebmp, smallbmp ]:
+ print('looking at {}'.format(fil), file=sys.stderr)
+ doFile(fil)
+
+def main():
+ argv = sys.argv
+ for first in range(1, len(argv), 3):
+ (face, largebmp, smallbmp) = argv[first:first+3]
+ doOne(face, largebmp, smallbmp)
+
+##############################################################################
+if __name__ == '__main__':
+ main()
diff --git a/xwords4/dawg/mkbyodbins.sh b/xwords4/dawg/mkbyodbins.sh
new file mode 100755
index 000000000..fedd03483
--- /dev/null
+++ b/xwords4/dawg/mkbyodbins.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+for INFO in $(ls */info.txt); do
+ DIR=$(dirname $INFO)
+ echo "*** processing $(basename $DIR) ***"
+ (cd $DIR && make clean byodbins)
+done
diff --git a/xwords4/dawg/par.pl b/xwords4/dawg/par.pl
deleted file mode 100755
index 024c58943..000000000
--- a/xwords4/dawg/par.pl
+++ /dev/null
@@ -1,234 +0,0 @@
-#!/usr/bin/perl
-
-# Copyright 2002 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 published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-# Only enough of par's features to support building a crosswords dict
-# pdb
-
-use strict;
-
-my $debug = 0;
-
-
-# stolen from par source
-my $PRC_FLAGS_RESOURCE = (0x1<<0);
-my $PRC_FLAGS_READONLY = (0x1<<1);
-my $PRC_FLAGS_DIRTY = (0x1<<2);
-my $PRC_FLAGS_BACKUP = (0x1<<3);
-my $PRC_FLAGS_NEWER = (0x1<<4);
-my $PRC_FLAGS_RESET = (0x1<<5);
-my $PRC_FLAGS_COPYPREVENT = (0x1<<6);
-my $PRC_FLAGS_STREAM = (0x1<<7);
-my $PRC_FLAGS_HIDDEN = (0x1<<8);
-my $PRC_FLAGS_LAUNCHABLE = (0x1<<9);
-my $PRC_FLAGS_RECYCLABLE = (0x1<<10);
-my $PRC_FLAGS_BUNDLE = (0x1<<11);
-my $PRC_FLAGS_OPEN = (0x1<<15);
-
-
-my $gAttrs = 0;
-my $gVersion = 1; # par defaults this to 1
-
-my $cmd = shift( @ARGV );
-die "only 'c' supported now" if $cmd ne "c" && $cmd ne "-c";
-
-readHOptions( \@ARGV );
-
-my $dbfile = shift( @ARGV );
-my $name = shift( @ARGV );
-die "name $name too long" if length($name) > 31;
-my $type = shift( @ARGV );
-die "type $type must be of length 4" if length($type) != 4;
-my $cid = shift( @ARGV );
-die "cid $cid must be of length 4" if length($cid) != 4;
-
-my @fileNames;
-my @fileLengths;
-
-my $nFiles = 0;
-
-while ( @ARGV > 0 ) {
- my $filename = shift( @ARGV );
- push @fileNames, $filename;
- push @fileLengths, -s $filename;
- ++$nFiles;
-}
-
-# from par's prcp.h; thanks djw!
-# typedef struct prc_file_t {
-# prc_byte_t name[32];
-# prc_byte_t flags[2];
-# prc_byte_t version[2];
-# prc_byte_t ctime[4];
-# prc_byte_t mtime[4];
-# prc_byte_t btime[4];
-# prc_byte_t modnum[4];
-# prc_byte_t appinfo[4];
-# prc_byte_t sortinfo[4];
-# prc_byte_t type[4];
-# prc_byte_t cid[4];
-# prc_byte_t unique_id_seed[4];
-# prc_byte_t next_record_list[4];
-# prc_byte_t nrecords[2];
-# } prc_file_t;
-
-my $str;
-my $offset = 0;
-
-open OUTFILE, "> $dbfile" or die "couldn't open outfile $dbfile for writing";
-
-# print the string, then pad with 0s
-$offset = length($name);
-print OUTFILE $name;
-while ( $offset < 32 ) {
- print OUTFILE pack("c", 0);
- ++$offset;
-}
-
-$str = pack("n", $gAttrs); # flags
-print OUTFILE $str;
-$offset += length($str);
-
-$str = pack("n", $gVersion); # version
-print OUTFILE $str;
-$offset += length($str);
-
-my $time = time() + 2082844800;
-$str = pack("NNN", $time, $time, 0); # ctime, mtime, btime
-print OUTFILE $str;
-$offset += length($str);
-
-$str = pack("N", 0 ); # mod num
-print OUTFILE $str;
-$offset += length($str);
-
-$str = pack("N", 0 ); # appinfo
-print OUTFILE $str;
-$offset += length($str);
-
-$str = pack("N", 0 ); # sortinfo
-print OUTFILE $str;
-$offset += length($str);
-
-
-print OUTFILE $type; # type
-print OUTFILE $cid; # cid
-$offset += 8;
-
-$str = pack("NN", 0, 0 ); # unique_id_seed, next_record_list
-print OUTFILE $str;
-$offset += length($str);
-
-$str = pack("n", $nFiles ); # nrecords
-print OUTFILE $str;
-$offset += length($str);
-
-$offset += $nFiles * 8;
-$offset += 2; # djw adds 2 bytes after size list; see below
-foreach my $len ( @fileLengths ) {
- print OUTFILE pack( "N", $offset );
- print OUTFILE pack( "N", 0 );
- $offset += $len;
-}
-
-print OUTFILE pack( "n", 0 ); # djw does this sans comment: flush.c, line 87
-
-foreach my $file ( @fileNames ) {
- open INFILE, "<$file" or die "couldn't open infile $file\n";
- my $buffer;
- while ( read INFILE, $buffer, 1024 ) {
- print OUTFILE $buffer;
- }
- close INFILE;
-}
-
-
-close OUTFILE;
-
-exit 0;
-
-##############################################################################
-# Subroutines
-##############################################################################
-
-sub readHOptions {
-
- my ( $argvR ) = @_;
-
- for ( ; ; ) {
- my $opt = ${$argvR}[0];
-
- if ( $opt !~ /^-/ ) {
- last;
- }
-
- # it starts with a '-': use it; else don't consume anything
- shift @{$argvR};
-
- if ( $opt eq "-a" ) {
- my $attrs = shift @{$argvR};
- processAttrString( $attrs );
- } elsif ( $opt eq "-v" ) {
- $gVersion = shift @{$argvR};
- } else {
- die "what's with \"$opt\": -a and -v are the only hattrs supported";
- }
- }
-
-} # readHOptions
-
-sub processAttrString {
-
- my ( $attrs ) = @_;
-
- foreach my $flag ( split /\|/, $attrs ) {
-
- print STDERR "looking at flag $flag\n" if $debug;
-
- if ( $flag =~ /resource/ ) {
- $gAttrs |= $PRC_FLAGS_RESOURCE;
- die "resource attr not supported";
- } elsif ( $flag =~ /readonly/ ) {
- $gAttrs |= $PRC_FLAGS_READONLY;
- } elsif ( $flag =~ /dirty/ ) {
- $gAttrs |= $PRC_FLAGS_DIRTY;
- } elsif ( $flag =~ /backup/ ) {
- $gAttrs |= $PRC_FLAGS_BACKUP;
- } elsif ( $flag =~ /newer/ ) {
- $gAttrs |= $PRC_FLAGS_NEWER;
- } elsif ( $flag =~ /reset/ ) {
- $gAttrs |= $PRC_FLAGS_RESET;
- } elsif ( $flag =~ /copyprevent/ ) {
- $gAttrs |= $PRC_FLAGS_COPYPREVENT;
- } elsif ( $flag =~ /stream/ ) {
- $gAttrs |= $PRC_FLAGS_STREAM;
- die "stream attr not supported";
- } elsif ( $flag =~ /hidden/ ) {
- $gAttrs |= $PRC_FLAGS_HIDDEN;
- } elsif ( $flag =~ /launchable/ ) {
- $gAttrs |= $PRC_FLAGS_LAUNCHABLE;
- } elsif ( $flag =~ /recyclable/ ) {
- $gAttrs |= $PRC_FLAGS_RECYCLABLE;
- } elsif ( $flag =~ /bundle/ ) {
- $gAttrs |= $PRC_FLAGS_BUNDLE;
- } elsif ( $flag =~ /open/ ) {
- $gAttrs |= $PRC_FLAGS_OPEN;
- } else {
- die "flag $flag not supportd";
- }
- }
-} # processAttrString
diff --git a/xwords4/dawg/pbitm2bin.py b/xwords4/dawg/pbitm2bin.py
new file mode 100755
index 000000000..2e8bd78f0
--- /dev/null
+++ b/xwords4/dawg/pbitm2bin.py
@@ -0,0 +1,68 @@
+#!/usr/bin/env python3
+#
+# Copyright 2001-2020 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 published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+#
+# Given a pbitm on stdin, a text bitmap file where '#' indicates a set
+# bit and '-' indicates a clear bit, convert into binary form (on
+# stdout) where there's one bit per bit plus a byte each for the width
+# and height. Nothing for bitdepth at this point. And no padding: if
+# the number of bits in a row isn't a multiple of 8 then one byte will
+# hold the last bits of one row and the first of another.
+
+import os, struct, sys
+
+lines = [ line.strip() for line in sys.stdin ]
+nRows = len(lines)
+nCols = 0
+bits = ''
+
+# first gather information and sanity-check the data
+
+for line in lines:
+ lineLen = len(line)
+ if nCols == 0:
+ nCols = lineLen
+ else:
+ assert nCols == lineLen, 'line of inconsistent length'
+ bits += line
+
+with os.fdopen(sys.stdout.fileno(), "wb", closefd=False) as stdout:
+ stdout.write(struct.pack('B', nCols))
+
+ # if we've been given an empty file, print out a single null byte
+ # and be done. That'll be the convention for "non-existant
+ # bitmap".
+
+ if nCols > 0:
+ stdout.write(struct.pack( 'B', nRows ) )
+ print( 'emitting {}x{} bitmap'.format(nCols, nRows), file=sys.stderr)
+
+ while bits:
+ cur = bits[:8]
+ bits = bits[8:]
+
+ byt = 0
+ for indx in range(len(cur)):
+ ch = cur[indx]
+ assert ch == '-' or ch == '#', "char {} neither '#' nor '-'".format(ch)
+ if ch == '#': byt |= 1 << (7 - indx)
+
+ stdout.write(struct.pack( 'B', byt ))
+
+ stdout.flush()
diff --git a/xwords4/dawg/xloc.pl b/xwords4/dawg/xloc.pl
deleted file mode 100755
index 23ef0ca43..000000000
--- a/xwords4/dawg/xloc.pl
+++ /dev/null
@@ -1,76 +0,0 @@
-#!/usr/bin/perl
-
-# Copyright 2002 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 published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-# test and wrapper file for xloc.pm
-
-use strict;
-use xloc;
-
-my $unicode = -1;
-my $doval = 0;
-my $dosize = 0;
-my $enc;
-my $outfile;
-
-my $arg;
-while ( $arg = $ARGV[0] ) {
- if ( $arg eq '-enc' ) {
- $enc = $ARGV[1];
- shift @ARGV;
- } elsif ( $arg eq "-tn" ) {
- $unicode = 1;
- } elsif ( $arg eq "-t" ) {
- $unicode = 0;
- } elsif ( $arg eq "-v" ) {
- $doval = 1;
- } elsif ( $arg eq "-s" ) {
- $dosize = 1;
- } elsif ( $arg eq '-out' ) {
- $outfile = $ARGV[1];
- shift @ARGV;
- } else {
- die "unknown arg $arg\n";
- }
- shift @ARGV;
-}
-
-my $infoFile = "info.txt";
-
-die "info file $infoFile not found\n" if ! -s $infoFile;
-
-my $xlocToken = xloc::ParseTileInfo($infoFile, $enc);
-
-if ( $enc ) {
- open OUTFILE, ">:encoding($enc)", "$outfile"
- or die "couldn't open $outfile";
-} else {
- open OUTFILE, ">$outfile" or die "couldn't open $outfile";
-}
-# For f*cking windoze linefeeds
-# binmode( OUTFILE );
-
-if ( $unicode ne -1 ) {
- xloc::WriteMapFile( $xlocToken, $unicode, \*OUTFILE );
-} elsif ( $dosize ) {
- my $count = xloc::GetNTiles( $xlocToken );
- print OUTFILE pack("c", $count );
-} elsif ( $doval ) {
- xloc::WriteValuesFile( $xlocToken, \*OUTFILE );
-}
-
-close OUTFILE;
diff --git a/xwords4/dawg/xloc.pm b/xwords4/dawg/xloc.pm
deleted file mode 100644
index 2bcb624b5..000000000
--- a/xwords4/dawg/xloc.pm
+++ /dev/null
@@ -1,194 +0,0 @@
-#!/usr/bin/perl
-
-# Copyright 2002-2014 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 published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-# The idea here is that all that matters about a language is stored in
-# one file (possibly excepting rules for prepping a dictionary).
-# There's a list of tile faces, counts and values, and also some
-# name-value pairs as needed. The pairs come first, and then a list
-# of tiles.
-
-package xloc;
-
-use strict;
-use warnings;
-# force output in utf8
-use open qw/:std :utf8/;
-
-BEGIN {
- use Exporter ();
- our ($VERSION, @ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS);
-
- $VERSION = 1.00;
-
- @ISA = qw(Exporter);
- @EXPORT = qw(&ParseTileInfo &GetNTiles &TileFace &TileValue
- &TileCount &GetValue &WriteMapFile &WriteValuesFile);
- %EXPORT_TAGS = ( );
-}
-
-# Returns what's meant to be an opaque object that can be passed back
-# for queries. It's a hash with name-value pairs and an _INFO entry
-# containing a list of tile info lists.
-
-sub ParseTileInfo($$) {
- my ( $filePath, $enc ) = @_;
- my %result;
-
- if ( $enc ) {
- open( INPUT, "<:encoding($enc)", "$filePath" )
- or die "couldn't open $filePath";
- } else {
- open( INPUT, "<$filePath" )
- or die "couldn't open $filePath";
- }
-
- my $inTiles = 0;
- my @tiles;
- while ( ) {
-
- chomp;
- s/\#.*$//;
- s/^\s*$//; # nuke all-white-space lines
- next if !length;
-
- if ( $inTiles ) {
- if ( // ) {
- last;
- } else {
- my ( $count, $val, $face ) = m/^\s*(\w+)\s+(\w+)\s+(.*)\s*$/;
- push @tiles, [ $count, $val, $face ];
- }
- } elsif ( /\w:/ ) {
- my ( $nam, $val ) = split ':', $_, 2;
- $result{$nam} .= $val;
- } elsif ( // ) {
- $inTiles = 1;
- }
-
- }
-
- close INPUT;
-
- $result{"_TILES"} = [ @tiles ];
-
- return \%result;
-}
-
-sub GetNTiles($) {
- my ( $hashR ) = @_;
-
- my $listR = ${$hashR}{"_TILES"};
-
- return 0 + @{$listR};
-}
-
-sub GetValue($$) {
- my ( $hashR, $name ) = @_;
- return ${$hashR}{$name};
-}
-
-sub printLetters($$) {
- my ( $str, $fhr ) = @_;
- my @letters = split( /\|/, $str );
- $str = join( " ", @letters );
- for ( my $key = 0; $key < length($str); ++$key ) {
- my $chr = substr( $str, $key, 1 );
- print $fhr pack( "U", ord($chr) );
- }
-}
-
-sub WriteMapFile($$$) {
- my ( $hashR, $unicode, $fhr ) = @_;
-
- my $count = GetNTiles($hashR);
- my $specialCount = 0;
- for ( my $i = 0; $i < $count; ++$i ) {
- my $tileR = GetNthTile( $hashR, $i );
- my $str = ${$tileR}[2];
-
- if ( $str =~ /\'(.(\|.)*)\'/ ) {
- printLetters( $1, $fhr );
- } elsif ( $str =~ /\"(.+)\"/ ) {
- print $fhr pack( "c", $specialCount++ );
- } elsif ( $str =~ /(\d+)/ ) {
- print $fhr pack( "n", $1 );
- } else {
- die "WriteMapFile: unrecognized face format $str, elem $i";
- }
- }
-} # WriteMapFile
-
-sub WriteValuesFile($$) {
- my ( $hashR, $fhr ) = @_;
-
- my $header = GetValue( $hashR,"XLOC_HEADER" );
- die "no XLOC_HEADER found" if ! $header;
-
- print STDERR "header is $header\n";
-
- print $fhr pack( "n", hex($header) );
-
- my $count = GetNTiles($hashR);
- for ( my $i = 0; $i < $count; ++$i ) {
- my $tileR = GetNthTile( $hashR, $i );
-
- print $fhr pack( "c", TileValue($tileR) );
- print $fhr pack( "c", TileCount($tileR) );
- }
-
-} # WriteValuesFile
-
-sub GetNthTile($$) {
- my ( $hashR, $n ) = @_;
- my $listR = ${$hashR}{"_TILES"};
-
- return ${$listR}[$n];
-}
-
-sub TileFace($) {
- my ( $tileR ) = @_;
- my $result;
-
- my $str = ${$tileR}[2];
-
- if ( $str =~ /\'(.(\|.)*)\'/ ) {
- $result = $1;
- } elsif ( $str =~ /\"(.+)\"/ ) {
- $result = $1;
- } elsif ( $str =~ /(\d+)/ ) {
- $result = chr($1);
- } else {
- die "TileFace: unrecognized face format: $str";
- }
- return $result;
-}
-
-sub TileValue($) {
- my ( $tileR ) = @_;
-
- return ${$tileR}[0];
-}
-
-sub TileCount($) {
- my ( $tileR ) = @_;
-
- return ${$tileR}[1];
-}
-
-1;
diff --git a/xwords4/dawg/xloc.py b/xwords4/dawg/xloc.py
new file mode 100755
index 000000000..c329ebd34
--- /dev/null
+++ b/xwords4/dawg/xloc.py
@@ -0,0 +1,132 @@
+#!/usr/bin/env python3
+import argparse, os, re, struct, sys
+
+def errorOut(msg):
+ print('ERROR: {}'.format(msg))
+ sys.exit(1)
+
+def mkParser():
+ parser = argparse.ArgumentParser()
+ parser.add_argument('-enc', dest = 'ENCODING', type = str, help = 'use this encoding' )
+ parser.add_argument('-tn', dest = 'DO_TABLE', action = 'store_true', help = 'output table file' )
+
+ # parser.add_argument('-tn', dest = 'UNICODE', default = False,
+ # action = 'store_true', help = 'assume unicode')
+ # parser.add_argument('-t', dest = 'UNICODE', type = str, default = True,
+ # action = 'store_false', help = 'DO NOT assume unicode')
+ parser.add_argument('-v', dest = 'DO_VALS', action = 'store_true', help = 'output values file' )
+ parser.add_argument('-s', dest = 'DO_SIZE', action = 'store_true', help = 'output size file')
+ parser.add_argument('-out', dest = 'OUTFILE', type = str, help = 'outfile path')
+
+ parser.add_argument('--table-file', dest = 'TABLE_FILE', type = str, help = 'write table file here')
+ parser.add_argument('--size-file', dest = 'SIZE_FILE', type = str, help = 'write size file here')
+ parser.add_argument('--vals-file', dest = 'VALS_FILE', type = str, help = 'write vals file here')
+
+ return parser
+
+sPreComment = re.compile('^(.*)#.*$')
+sVarAssign = re.compile('^(\w+):(.*)$')
+sBeginTiles = re.compile('^$')
+sEndTiles = re.compile('^$')
+sSingleCharMatch = re.compile("'(.(\|.)*)'")
+sSpecialsMatch = re.compile('{"(.+)"(,.+)?}')
+
+def parseTileInfo(infoFile, encoding):
+ result = {'_TILES' : []}
+ with open(infoFile, 'rt') as file:
+ data = file.read()
+ # if encoding:
+ # data = data.decode(encoding)
+ data = data.split('\n')
+
+ inTiles = False
+ tiles = []
+ for line in data:
+ # print('line at start: {}'.format(line))
+ match = sPreComment.match(line)
+ if match:
+ line = match.group(1)
+ # print('line sans comment: {}'.format(line))
+ if 0 == len(line):continue
+
+ if inTiles:
+ if sEndTiles.match(line):
+ break
+ else:
+ (count, val, face) = line.split(None, 2)
+ result['_TILES'].append((count, val, face))
+ elif sBeginTiles.match(line):
+ inTiles = True
+ else:
+ match = sVarAssign.match(line)
+ if match:
+ var = match.group(1)
+ if not var in result: result[var] = ''
+ result[var] += match.group(2)
+
+ return result
+
+def printLetters( letters, outfile ):
+ letters = letters.split('|')
+ letters = ' '.join(letters)
+ outfile.write(letters.encode('utf8'))
+
+def writeMapFile(xlocToken, outfile):
+ print('writeMapFile()')
+ tiles = xlocToken['_TILES']
+ specialCount = 0
+ for tile in tiles:
+ face = tile[2]
+ match = sSingleCharMatch.match(face)
+ if match:
+ printLetters( match.group(1), outfile )
+ continue
+ match = sSpecialsMatch.match(face)
+ if match:
+ print('specials char: {}'.format(match.group(1)))
+ outfile.write(struct.pack('B', specialCount ))
+ specialCount += 1
+ continue
+
+ print('bad/unmatched face: {}'.format(face))
+ assert False
+
+def writeValuesFile(xlocToken, outfile):
+ header = xlocToken.get('XLOC_HEADER') or errorOut('no XLOC_HEADER found')
+
+ print('writing header: {}'.format(header))
+ outfile.write(struct.pack('!H', int(header, 16)))
+
+ for tile in xlocToken['_TILES']:
+ val = int(tile[0])
+ count = int(tile[1])
+ outfile.write(struct.pack('BB', val, count))
+
+def main():
+ print('{}.main {} called'.format(sys.argv[0], sys.argv[1:]))
+ args = mkParser().parse_args()
+
+ infoFile = 'info.txt'
+ if not os.path.exists(infoFile):
+ errorOut('{} not found'.format(infoFile))
+ xlocToken = parseTileInfo(infoFile, args.ENCODING)
+
+ if args.DO_TABLE or args.TABLE_FILE:
+ path = args.TABLE_FILE or args.OUTFILE
+ with open(path, 'wb') as outfile:
+ writeMapFile(xlocToken, outfile);
+
+ if args.DO_SIZE or args.SIZE_FILE:
+ path = args.SIZE_FILE or args.OUTFILE
+ with open(path, 'wb') as outfile:
+ count = len(xlocToken['_TILES'])
+ outfile.write(struct.pack('B', count))
+
+ if args.DO_VALS or args.VALS_FILE:
+ path = args.VALS_FILE or args.OUTFILE
+ with open(path, 'wb') as outfile:
+ writeValuesFile( xlocToken, outfile )
+
+##############################################################################
+if __name__ == '__main__':
+ main()
diff --git a/xwords4/linux/lindutil.c b/xwords4/linux/lindutil.c
index 9071074e6..4ac1d7a26 100644
--- a/xwords4/linux/lindutil.c
+++ b/xwords4/linux/lindutil.c
@@ -330,18 +330,20 @@ linux_dutil_loadPtr( XW_DUtilCtxt* duc, XWEnv XP_UNUSED(xwe), const XP_UCHAR* ke
*lenp = buflen;
} else {
gchar* tmp = XP_MALLOC( duc->mpool, buflen );
- res = db_fetch( pDb, key, tmp, &buflen );
+ gint tmpLen = buflen;
+ res = db_fetch( pDb, key, tmp, &tmpLen );
+ XP_ASSERT( buflen == tmpLen );
XP_ASSERT( res == SUCCESS );
XP_ASSERT( tmp[buflen-1] == '\0' );
gsize out_len;
- guchar* txt = g_base64_decode( (const gchar*)tmp, &out_len );
+ guchar* binp = g_base64_decode( tmp, &out_len );
if ( out_len <= *lenp ) {
- XP_MEMCPY( data, txt, out_len );
+ XP_MEMCPY( data, binp, out_len );
*lenp = out_len;
}
XP_FREEP( duc->mpool, &tmp );
- g_free( txt );
+ g_free( binp );
}
} else {
*lenp = 0; /* doesn't exist */
diff --git a/xwords4/linux/scripts/discon_ok2.py b/xwords4/linux/scripts/discon_ok2.py
index 370a8be80..b2703faae 100755
--- a/xwords4/linux/scripts/discon_ok2.py
+++ b/xwords4/linux/scripts/discon_ok2.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
-import re, os, sys, getopt, shutil, threading, requests, json, glob
+import re, os, sys, shutil, threading, requests, json, glob
import argparse, datetime, random, signal, subprocess, time
from shutil import rmtree