mirror of
git://xwords.git.sourceforge.net/gitroot/xwords/xwords
synced 2024-12-28 09:58:30 +01:00
Merge branch 'android_branch' into android_bt
Conflicts: xwords4/android/XWords4/src/org/eehouse/android/xw4/DlgDelegate.java xwords4/linux/Makefile
This commit is contained in:
commit
cd8889f90e
22 changed files with 189 additions and 163 deletions
|
@ -84,6 +84,11 @@
|
|||
>
|
||||
</activity>
|
||||
|
||||
<activity android:name="LookupActivity"
|
||||
android:configChanges="keyboardHidden|orientation"
|
||||
android:theme="@android:style/Theme.Dialog"
|
||||
/>
|
||||
|
||||
<receiver android:name="RelayReceiver">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED"/>
|
||||
|
|
|
@ -291,16 +291,15 @@ makeStringArray( JNIEnv *env, int siz, const XP_UCHAR** vals )
|
|||
}
|
||||
|
||||
jstring
|
||||
streamToJString( MPFORMAL JNIEnv *env, XWStreamCtxt* stream )
|
||||
streamToJString( JNIEnv *env, XWStreamCtxt* stream )
|
||||
{
|
||||
int len = stream_getSize( stream );
|
||||
XP_UCHAR* buf = XP_MALLOC( mpool, 1 + len );
|
||||
XP_UCHAR buf[1 + len];
|
||||
stream_getBytes( stream, buf, len );
|
||||
buf[len] = '\0';
|
||||
|
||||
jstring jstr = (*env)->NewStringUTF( env, buf );
|
||||
|
||||
XP_FREE( mpool, buf );
|
||||
return jstr;
|
||||
}
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ void setBoolArray( JNIEnv* env, jbooleanArray jarr, int count,
|
|||
const jboolean* vals );
|
||||
|
||||
jobjectArray makeStringArray( JNIEnv *env, int size, const XP_UCHAR** vals );
|
||||
jstring streamToJString( MPFORMAL JNIEnv* env, XWStreamCtxt* stream );
|
||||
jstring streamToJString( JNIEnv* env, XWStreamCtxt* stream );
|
||||
|
||||
/* Note: jmethodID can be cached. Should not look up more than once. */
|
||||
jmethodID getMethodID( JNIEnv* env, jobject obj, const char* proc,
|
||||
|
|
|
@ -126,7 +126,7 @@ and_util_userQuery( XW_UtilCtxt* uc, UtilQueryID id, XWStreamCtxt* stream )
|
|||
|
||||
jstring jstr = NULL;
|
||||
if ( NULL != stream ) {
|
||||
jstr = streamToJString( MPPARM(util->util.mpool) env, stream );
|
||||
jstr = streamToJString( env, stream );
|
||||
}
|
||||
result = (*env)->CallBooleanMethod( env, util->jutil, mid, id, jstr );
|
||||
if ( NULL != jstr ) {
|
||||
|
@ -251,8 +251,8 @@ and_util_informMove( XW_UtilCtxt* uc, XWStreamCtxt* expl, XWStreamCtxt* words )
|
|||
{
|
||||
if ( !!words ) {
|
||||
UTIL_CBK_HEADER( "informMove", "(Ljava/lang/String;Ljava/lang/String;)V" );
|
||||
jstring jexpl = streamToJString( MPPARM(util->util.mpool) env, expl );
|
||||
jstring jwords = streamToJString( MPPARM(util->util.mpool) env, words );
|
||||
jstring jexpl = streamToJString( env, expl );
|
||||
jstring jwords = streamToJString( env, words );
|
||||
(*env)->CallVoidMethod( env, util->jutil, mid, jexpl, jwords );
|
||||
(*env)->DeleteLocalRef( env, jexpl );
|
||||
(*env)->DeleteLocalRef( env, jwords );
|
||||
|
@ -454,8 +454,7 @@ and_util_cellSquareHeld( XW_UtilCtxt* uc, XWStreamCtxt* words )
|
|||
{
|
||||
if ( NULL != words ) {
|
||||
UTIL_CBK_HEADER( "cellSquareHeld", "(Ljava/lang/String;)V" );
|
||||
jstring jwords =
|
||||
streamToJString( MPPARM(util->util.mpool) env, words );
|
||||
jstring jwords = streamToJString( env, words );
|
||||
(*env)->CallVoidMethod( env, util->jutil, mid, jwords );
|
||||
(*env)->DeleteLocalRef( env, jwords );
|
||||
UTIL_CBK_TAIL();
|
||||
|
|
|
@ -871,7 +871,7 @@ Java_org_eehouse_android_xw4_jni_XwJNI_board_1formatRemainingTiles
|
|||
XWStreamCtxt* stream = mem_stream_make( MPPARM(mpool) globals->vtMgr,
|
||||
NULL, 0, NULL );
|
||||
board_formatRemainingTiles( state->game.board, stream );
|
||||
result = streamToJString( MPPARM(mpool) env, stream );
|
||||
result = streamToJString( env, stream );
|
||||
stream_destroy( stream );
|
||||
(*env)->DeleteLocalRef( env, result );
|
||||
|
||||
|
@ -887,7 +887,7 @@ Java_org_eehouse_android_xw4_jni_XwJNI_server_1formatDictCounts
|
|||
XWJNI_START_GLOBALS();
|
||||
XWStreamCtxt* stream = and_empty_stream( MPPARM(mpool) globals );
|
||||
server_formatDictCounts( state->game.server, stream, nCols );
|
||||
result = streamToJString( MPPARM(mpool) env, stream );
|
||||
result = streamToJString( env, stream );
|
||||
stream_destroy( stream );
|
||||
(*env)->DeleteLocalRef( env, result );
|
||||
XWJNI_END();
|
||||
|
@ -914,7 +914,7 @@ Java_org_eehouse_android_xw4_jni_XwJNI_model_1writeGameHistory
|
|||
XWStreamCtxt* stream = and_empty_stream( MPPARM(mpool) globals );
|
||||
model_writeGameHistory( state->game.model, stream, state->game.server,
|
||||
gameOver );
|
||||
result = streamToJString( MPPARM(mpool) env, stream );
|
||||
result = streamToJString( env, stream );
|
||||
(*env)->DeleteLocalRef( env, result );
|
||||
stream_destroy( stream );
|
||||
XWJNI_END();
|
||||
|
@ -960,7 +960,7 @@ Java_org_eehouse_android_xw4_jni_XwJNI_server_1writeFinalScores
|
|||
XWJNI_START_GLOBALS();
|
||||
XWStreamCtxt* stream = and_empty_stream( MPPARM(mpool) globals );
|
||||
server_writeFinalScores( state->game.server, stream );
|
||||
result = streamToJString( MPPARM(mpool) env, stream );
|
||||
result = streamToJString( env, stream );
|
||||
(*env)->DeleteLocalRef( env, result );
|
||||
stream_destroy( stream );
|
||||
XWJNI_END();
|
||||
|
|
|
@ -1,13 +1,18 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<org.eehouse.android.xw4.LookupView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
>
|
||||
|
||||
<ListView android:id="@+id/lookup_list"
|
||||
<TextView android:id="@+id/summary"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="fill_parent"
|
||||
android:paddingLeft="8dp"
|
||||
/>
|
||||
|
||||
<ListView android:id="@+id/android:list"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:drawSelectorOnTop="false"
|
||||
|
@ -19,4 +24,4 @@
|
|||
android:layout_height="wrap_content"
|
||||
/>
|
||||
|
||||
</org.eehouse.android.xw4.LookupView>
|
||||
</LinearLayout>
|
||||
|
|
|
@ -45,8 +45,7 @@ public class DlgDelegate {
|
|||
public static final int CONFIRM_THEN = 4;
|
||||
public static final int TEXT_OR_HTML_THEN = 5;
|
||||
public static final int DLG_DICTGONE = 6;
|
||||
public static final int DLG_LOOKUP = 7;
|
||||
public static final int DIALOG_LAST = DLG_LOOKUP;
|
||||
public static final int DIALOG_LAST = DLG_DICTGONE;
|
||||
|
||||
public static final int SMS_BTN = AlertDialog.BUTTON_POSITIVE;
|
||||
public static final int EMAIL_BTN = AlertDialog.BUTTON_NEGATIVE;
|
||||
|
@ -77,9 +76,6 @@ public class DlgDelegate {
|
|||
private Activity m_activity;
|
||||
private DlgClickNotify m_clickCallback;
|
||||
private String m_dictName = null;
|
||||
private String[] m_words = null;
|
||||
private int m_wordsLang = -1;
|
||||
private boolean m_forceList = false;
|
||||
private ProgressDialog m_progress;
|
||||
private Handler m_handler;
|
||||
|
||||
|
@ -95,9 +91,6 @@ public class DlgDelegate {
|
|||
m_cbckID = bundle.getInt( CALLBACK );
|
||||
m_posButton = bundle.getInt( POSBUTTON );
|
||||
m_prefsKey = bundle.getInt( PREFSKEY );
|
||||
m_words = bundle.getStringArray( WORDS );
|
||||
m_wordsLang = bundle.getInt( LANG );
|
||||
m_forceList = bundle.getBoolean( FORCELIST );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -107,9 +100,6 @@ public class DlgDelegate {
|
|||
outState.putInt( CALLBACK, m_cbckID );
|
||||
outState.putInt( POSBUTTON, m_posButton );
|
||||
outState.putInt( PREFSKEY, m_prefsKey );
|
||||
outState.putStringArray( WORDS, m_words );
|
||||
outState.putInt( LANG, m_wordsLang );
|
||||
outState.putBoolean( FORCELIST, m_forceList );
|
||||
}
|
||||
|
||||
public Dialog onCreateDialog( int id )
|
||||
|
@ -134,15 +124,6 @@ public class DlgDelegate {
|
|||
case DLG_DICTGONE:
|
||||
dialog = createDictGoneDialog();
|
||||
break;
|
||||
case DLG_LOOKUP:
|
||||
LookupView view = (LookupView)Utils.inflate( m_activity,
|
||||
R.layout.lookup );
|
||||
dialog = new AlertDialog.Builder( m_activity )
|
||||
.setView( view )
|
||||
.create();
|
||||
view.setDialog( dialog, DLG_LOOKUP );
|
||||
view.setWords( m_words, m_wordsLang, m_forceList );
|
||||
break;
|
||||
}
|
||||
return dialog;
|
||||
}
|
||||
|
@ -259,11 +240,11 @@ public class DlgDelegate {
|
|||
|
||||
public void launchLookup( String[] words, int lang, boolean forceList )
|
||||
{
|
||||
Assert.assertTrue( null != words && 0 < words.length );
|
||||
m_words = words;
|
||||
m_wordsLang = lang;
|
||||
m_forceList = forceList;
|
||||
m_activity.showDialog( DLG_LOOKUP );
|
||||
Intent intent = new Intent( m_activity, LookupActivity.class );
|
||||
intent.putExtra( LookupActivity.WORDS, words );
|
||||
intent.putExtra( LookupActivity.LANG, lang );
|
||||
|
||||
m_activity.startActivity( intent );
|
||||
}
|
||||
|
||||
public void startProgress( int id )
|
||||
|
|
|
@ -20,9 +20,7 @@
|
|||
|
||||
package org.eehouse.android.xw4;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Resources;
|
||||
import android.net.Uri;
|
||||
|
@ -32,19 +30,21 @@ import android.view.Window;
|
|||
import android.widget.AdapterView;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.Button;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
import android.app.AlertDialog;
|
||||
import java.util.ArrayList;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
public class LookupView extends LinearLayout
|
||||
public class LookupActivity extends XWListActivity
|
||||
implements View.OnClickListener,
|
||||
AdapterView.OnItemClickListener,
|
||||
DialogInterface.OnDismissListener {
|
||||
AdapterView.OnItemClickListener {
|
||||
|
||||
public static final String WORDS = "WORDS";
|
||||
public static final String LANG = "LANG";
|
||||
public static final String FORCELIST = "FORCELIST";
|
||||
private static final String STATE = "STATE";
|
||||
private static final String WORDINDEX = "WORDINDEX";
|
||||
private static final String URLINDEX = "URLINDEX";
|
||||
|
||||
private static final int STATE_DONE = 0;
|
||||
private static final int STATE_WORDS = 1;
|
||||
|
@ -55,55 +55,66 @@ public class LookupView extends LinearLayout
|
|||
private static String[] s_lookupNames;
|
||||
private static String[] s_lookupUrls;
|
||||
private static ArrayAdapter<String> s_urlsAdapter;
|
||||
private static final int LIST_LAYOUT = // android.R.layout.simple_list_item_1;
|
||||
android.R.layout.select_dialog_item;
|
||||
//R.layout.select_dialog_item;
|
||||
private static final int LIST_LAYOUT = android.R.layout.simple_list_item_1;
|
||||
// android.R.layout.select_dialog_item;
|
||||
|
||||
private static int s_lang = -1;
|
||||
|
||||
private String[] m_words;
|
||||
private boolean m_forceList;
|
||||
private static int m_lang;
|
||||
private int m_wordIndex = 0;
|
||||
private int m_urlIndex = 0;
|
||||
private int m_state;
|
||||
private ArrayAdapter<String> m_wordsAdapter;
|
||||
private Button m_doneButton;
|
||||
private Dialog m_dialog;
|
||||
private Context m_context;
|
||||
private int m_dlgId;
|
||||
private ListView m_list;
|
||||
private TextView m_summary;
|
||||
|
||||
public LookupView( Context cx, AttributeSet as ) {
|
||||
super( cx, as );
|
||||
m_context = cx;
|
||||
}
|
||||
|
||||
public void setWords( String[] words, int lang, boolean forceList )
|
||||
@Override
|
||||
protected void onCreate( Bundle savedInstanceState )
|
||||
{
|
||||
m_words = words;
|
||||
m_forceList = forceList;
|
||||
setLang( lang );
|
||||
super.onCreate( savedInstanceState );
|
||||
requestWindowFeature( Window.FEATURE_NO_TITLE );
|
||||
setContentView( R.layout.lookup );
|
||||
|
||||
Intent intent = getIntent();
|
||||
m_words = intent.getStringArrayExtra( WORDS );
|
||||
setLang( intent.getIntExtra( LANG, -1 ) );
|
||||
m_forceList = intent.getBooleanExtra( FORCELIST, false );
|
||||
|
||||
m_state = STATE_DONE;
|
||||
adjustState( 1 );
|
||||
|
||||
m_wordsAdapter = new ArrayAdapter<String>( m_context, LIST_LAYOUT,
|
||||
m_wordsAdapter = new ArrayAdapter<String>( this, LIST_LAYOUT,
|
||||
m_words );
|
||||
m_list = (ListView)findViewById( R.id.lookup_list );
|
||||
m_list.setOnItemClickListener( this );
|
||||
getListView().setOnItemClickListener( this );
|
||||
|
||||
m_doneButton = (Button)findViewById( R.id.button_done );
|
||||
m_doneButton.setOnClickListener( this );
|
||||
|
||||
m_summary = (TextView)findViewById( R.id.summary );
|
||||
|
||||
switchState();
|
||||
}
|
||||
|
||||
public void setDialog( Dialog dialog, int id )
|
||||
@Override
|
||||
protected void onSaveInstanceState( Bundle outState )
|
||||
{
|
||||
m_dialog = dialog;
|
||||
m_dlgId = id;
|
||||
m_dialog.setOnDismissListener( this );
|
||||
super.onSaveInstanceState( outState );
|
||||
outState.putInt( STATE, m_state );
|
||||
outState.putInt( WORDINDEX, m_wordIndex );
|
||||
outState.putInt( URLINDEX, m_urlIndex );
|
||||
}
|
||||
|
||||
private void getBundledData( Bundle bundle )
|
||||
{
|
||||
if ( null == bundle ) {
|
||||
m_state = STATE_DONE;
|
||||
adjustState( 1 );
|
||||
} else {
|
||||
m_state = bundle.getInt( STATE );
|
||||
m_wordIndex = bundle.getInt( WORDINDEX );
|
||||
m_urlIndex = bundle.getInt( URLINDEX );
|
||||
}
|
||||
}
|
||||
|
||||
/* View.OnClickListener -- just the Done button */
|
||||
|
@ -126,12 +137,6 @@ public class LookupView extends LinearLayout
|
|||
switchState( 1 );
|
||||
}
|
||||
|
||||
/* DialogInterface.OnDismissListener interface */
|
||||
public void onDismiss( DialogInterface di )
|
||||
{
|
||||
m_dialog.getOwnerActivity().removeDialog( m_dlgId );
|
||||
}
|
||||
|
||||
private void adjustState( int incr )
|
||||
{
|
||||
m_state += incr;
|
||||
|
@ -160,17 +165,17 @@ public class LookupView extends LinearLayout
|
|||
{
|
||||
switch( m_state ) {
|
||||
case STATE_DONE:
|
||||
m_dialog.dismiss();
|
||||
finish();
|
||||
break;
|
||||
case STATE_WORDS:
|
||||
m_list.setAdapter( m_wordsAdapter );
|
||||
getListView().setAdapter( m_wordsAdapter );
|
||||
setSummary( R.string.title_lookup );
|
||||
m_doneButton.setText( R.string.button_done );
|
||||
break;
|
||||
case STATE_URLS:
|
||||
m_list.setAdapter( s_urlsAdapter );
|
||||
getListView().setAdapter( s_urlsAdapter );
|
||||
setSummary( m_words[m_wordIndex] );
|
||||
String txt = Utils.format( m_context, R.string.button_donef,
|
||||
String txt = Utils.format( this, R.string.button_donef,
|
||||
m_words[m_wordIndex] );
|
||||
m_doneButton.setText( txt );
|
||||
break;
|
||||
|
@ -182,7 +187,7 @@ public class LookupView extends LinearLayout
|
|||
Assert.fail();
|
||||
break;
|
||||
}
|
||||
} // adjustState
|
||||
} // switchState
|
||||
|
||||
private void lookupWord( String word, String fmt )
|
||||
{
|
||||
|
@ -196,14 +201,14 @@ public class LookupView extends LinearLayout
|
|||
intent.setFlags( Intent.FLAG_ACTIVITY_NEW_TASK );
|
||||
|
||||
try {
|
||||
m_context.startActivity( intent );
|
||||
startActivity( intent );
|
||||
} catch ( android.content.ActivityNotFoundException anfe ) {
|
||||
DbgUtils.logf( "%s", anfe.toString() );
|
||||
}
|
||||
}
|
||||
} // lookupWord
|
||||
|
||||
public void setLang( int lang )
|
||||
private void setLang( int lang )
|
||||
{
|
||||
if ( null == s_langCodes ) {
|
||||
s_langCodes = getResources().getStringArray( R.array.language_codes );
|
||||
|
@ -223,20 +228,20 @@ public class LookupView extends LinearLayout
|
|||
}
|
||||
s_lookupNames = tmpNames.toArray( new String[tmpNames.size()] );
|
||||
s_lookupUrls = tmpUrls.toArray( new String[tmpUrls.size()] );
|
||||
s_urlsAdapter = new ArrayAdapter<String>( m_context, LIST_LAYOUT,
|
||||
s_urlsAdapter = new ArrayAdapter<String>( this, LIST_LAYOUT,
|
||||
s_lookupNames );
|
||||
s_lang = lang;
|
||||
} // initLookup
|
||||
}
|
||||
}
|
||||
|
||||
private void setSummary( int id )
|
||||
{
|
||||
m_dialog.setTitle( m_context.getString( id ) );
|
||||
m_summary.setText( getString( id ) );
|
||||
}
|
||||
|
||||
private void setSummary( String word )
|
||||
{
|
||||
String title = Utils.format( m_context, R.string.pick_url_titlef, word );
|
||||
m_dialog.setTitle( title );
|
||||
String title = Utils.format( this, R.string.pick_url_titlef, word );
|
||||
m_summary.setText( title );
|
||||
}
|
||||
}
|
|
@ -288,7 +288,7 @@ board_makeFromStream( MPFORMAL XWStreamCtxt* stream, ModelCtxt* model,
|
|||
} /* board_makeFromStream */
|
||||
|
||||
void
|
||||
board_writeToStream( BoardCtxt* board, XWStreamCtxt* stream )
|
||||
board_writeToStream( const BoardCtxt* board, XWStreamCtxt* stream )
|
||||
{
|
||||
XP_U16 nPlayers, ii;
|
||||
XP_U16 nColsNBits;
|
||||
|
@ -319,8 +319,8 @@ board_writeToStream( BoardCtxt* board, XWStreamCtxt* stream )
|
|||
nPlayers = board->gi->nPlayers;
|
||||
|
||||
for ( ii = 0; ii < nPlayers; ++ii ) {
|
||||
PerTurnInfo* pti = &board->pti[ii];
|
||||
BoardArrow* arrow = &pti->boardArrow;
|
||||
const PerTurnInfo* pti = &board->pti[ii];
|
||||
const BoardArrow* arrow = &pti->boardArrow;
|
||||
stream_putBits( stream, nColsNBits, arrow->col );
|
||||
stream_putBits( stream, nColsNBits, arrow->row );
|
||||
stream_putBits( stream, 1, arrow->vert );
|
||||
|
|
|
@ -67,7 +67,7 @@ BoardCtxt* board_makeFromStream( MPFORMAL XWStreamCtxt* stream,
|
|||
|
||||
void board_destroy( BoardCtxt* board );
|
||||
|
||||
void board_writeToStream( BoardCtxt* board, XWStreamCtxt* stream );
|
||||
void board_writeToStream( const BoardCtxt* board, XWStreamCtxt* stream );
|
||||
|
||||
void board_setPos( BoardCtxt* board, XP_U16 left, XP_U16 top,
|
||||
XP_U16 width, XP_U16 height, XP_U16 maxCellSize,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
|
||||
/* -*- compile-command: "cd ../linux && make -j3 MEMDEBUG=TRUE"; -*- */
|
||||
/*
|
||||
* Copyright 2006 by Eric House (xwords@eehouse.org). All rights reserved.
|
||||
* Copyright 2006-2012 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
|
||||
|
@ -73,3 +74,22 @@ BoardObjectType_2str( BoardObjectType obj )
|
|||
|
||||
#endif /* ENABLE_LOGGING */
|
||||
|
||||
#ifdef DEBUG
|
||||
void
|
||||
dbg_logstream( XWStreamCtxt* stream, const char* func, int line )
|
||||
{
|
||||
if ( !!stream ) {
|
||||
XWStreamPos pos = stream_getPos( stream, POS_READ );
|
||||
XP_U16 len = stream_getSize( stream );
|
||||
XP_UCHAR buf[len+1];
|
||||
stream_getBytes( stream, buf, len );
|
||||
buf[len] = '\0';
|
||||
XP_LOGF( "stream %p at pos %lx from line %d of func %s: \"%s\"", stream,
|
||||
pos, line, func, buf );
|
||||
(void)stream_setPos( stream, POS_READ, pos );
|
||||
} else {
|
||||
XP_LOGF( "stream from line %d of func %s is null",
|
||||
line, func );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -26,4 +26,12 @@ const char* XP_Key_2str( XP_Key key );
|
|||
const char* DrawFocusState_2str( DrawFocusState dfs );
|
||||
const char* BoardObjectType_2str( BoardObjectType dfs );
|
||||
|
||||
# ifdef DEBUG
|
||||
void dbg_logstream( XWStreamCtxt* stream, const char* func, int line );
|
||||
# define XP_LOGSTREAM( s ) \
|
||||
dbg_logstream( s, __func__, __LINE__ )
|
||||
# else
|
||||
# define XP_LOGSTREAM( s )
|
||||
# endif
|
||||
|
||||
#endif
|
||||
|
|
|
@ -292,7 +292,7 @@ mem_stream_putBits( XWStreamCtxt* p_sctx, XP_U16 nBits, XP_U32 data
|
|||
} /* mem_stream_putBits */
|
||||
|
||||
static void
|
||||
mem_stream_copyFromStream( XWStreamCtxt* p_sctx, XWStreamCtxt* src,
|
||||
mem_stream_getFromStream( XWStreamCtxt* p_sctx, XWStreamCtxt* src,
|
||||
XP_U16 nBytes )
|
||||
{
|
||||
while ( nBytes > 0 ) {
|
||||
|
@ -305,7 +305,7 @@ mem_stream_copyFromStream( XWStreamCtxt* p_sctx, XWStreamCtxt* src,
|
|||
stream_putBytes( p_sctx, buf, len );
|
||||
nBytes -= len;
|
||||
}
|
||||
} /* mem_stream_copyFromStream */
|
||||
} /* mem_stream_getFromStream */
|
||||
|
||||
static void
|
||||
mem_stream_open( XWStreamCtxt* p_sctx )
|
||||
|
@ -333,7 +333,7 @@ mem_stream_close( XWStreamCtxt* p_sctx )
|
|||
static XP_U16
|
||||
mem_stream_getSize( const XWStreamCtxt* p_sctx )
|
||||
{
|
||||
MemStreamCtxt* stream = (MemStreamCtxt*)p_sctx;
|
||||
const MemStreamCtxt* stream = (const MemStreamCtxt*)p_sctx;
|
||||
XP_U16 size = stream->nBytesWritten - stream->curReadPos;
|
||||
return size;
|
||||
} /* mem_stream_getSize */
|
||||
|
@ -341,14 +341,14 @@ mem_stream_getSize( const XWStreamCtxt* p_sctx )
|
|||
static const XP_U8*
|
||||
mem_stream_getPtr( const XWStreamCtxt* p_sctx )
|
||||
{
|
||||
MemStreamCtxt* stream = (MemStreamCtxt*)p_sctx;
|
||||
const MemStreamCtxt* stream = (const MemStreamCtxt*)p_sctx;
|
||||
return stream->buf;
|
||||
} /* mem_stream_getPtr */
|
||||
|
||||
static XP_PlayerAddr
|
||||
mem_stream_getAddress( XWStreamCtxt* p_sctx )
|
||||
mem_stream_getAddress( const XWStreamCtxt* p_sctx )
|
||||
{
|
||||
MemStreamCtxt* stream = (MemStreamCtxt*)p_sctx;
|
||||
const MemStreamCtxt* stream = (const MemStreamCtxt*)p_sctx;
|
||||
return stream->channelNo;
|
||||
} /* mem_stream_getAddress */
|
||||
|
||||
|
@ -357,7 +357,7 @@ mem_stream_setAddress( XWStreamCtxt* p_sctx, XP_PlayerAddr channelNo )
|
|||
{
|
||||
MemStreamCtxt* stream = (MemStreamCtxt*)p_sctx;
|
||||
stream->channelNo = channelNo;
|
||||
} /* mem_stream_getAddress */
|
||||
} /* mem_stream_setAddress */
|
||||
|
||||
static void
|
||||
mem_stream_setVersion( XWStreamCtxt* p_sctx, XP_U16 vers )
|
||||
|
@ -367,9 +367,9 @@ mem_stream_setVersion( XWStreamCtxt* p_sctx, XP_U16 vers )
|
|||
} /* mem_stream_setVersion */
|
||||
|
||||
static XP_U16
|
||||
mem_stream_getVersion( XWStreamCtxt* p_sctx )
|
||||
mem_stream_getVersion( const XWStreamCtxt* p_sctx )
|
||||
{
|
||||
MemStreamCtxt* stream = (MemStreamCtxt*)p_sctx;
|
||||
const MemStreamCtxt* stream = (const MemStreamCtxt*)p_sctx;
|
||||
return stream->version;
|
||||
} /* mem_stream_getVersion */
|
||||
|
||||
|
@ -381,10 +381,10 @@ mem_stream_setOnCloseProc( XWStreamCtxt* p_sctx, MemStreamCloseCallback proc )
|
|||
}
|
||||
|
||||
static XWStreamPos
|
||||
mem_stream_getPos( XWStreamCtxt* p_sctx, PosWhich which )
|
||||
mem_stream_getPos( const XWStreamCtxt* p_sctx, PosWhich which )
|
||||
{
|
||||
XWStreamPos result;
|
||||
MemStreamCtxt* stream = (MemStreamCtxt*)p_sctx;
|
||||
const MemStreamCtxt* stream = (const MemStreamCtxt*)p_sctx;
|
||||
|
||||
if ( which == POS_WRITE ) {
|
||||
result = (stream->curWritePos << 3) | stream->nWriteBits;
|
||||
|
@ -396,8 +396,9 @@ mem_stream_getPos( XWStreamCtxt* p_sctx, PosWhich which )
|
|||
} /* mem_stream_getPos */
|
||||
|
||||
static XWStreamPos
|
||||
mem_stream_setPos( XWStreamCtxt* p_sctx, XWStreamPos newpos, PosWhich which )
|
||||
mem_stream_setPos( XWStreamCtxt* p_sctx, PosWhich which, XWStreamPos newpos )
|
||||
{
|
||||
XP_ASSERT( END_OF_STREAM != newpos ); /* not handling this yet */
|
||||
MemStreamCtxt* stream = (MemStreamCtxt*)p_sctx;
|
||||
XWStreamPos oldPos = mem_stream_getPos( p_sctx, which );
|
||||
|
||||
|
@ -448,7 +449,7 @@ make_vtable( MemStreamCtxt* stream )
|
|||
SET_VTABLE_ENTRY( vtable, stream_putU32, mem );
|
||||
SET_VTABLE_ENTRY( vtable, stream_putBits, mem );
|
||||
|
||||
SET_VTABLE_ENTRY( vtable, stream_copyFromStream, mem );
|
||||
SET_VTABLE_ENTRY( vtable, stream_getFromStream, mem );
|
||||
|
||||
SET_VTABLE_ENTRY( vtable, stream_setPos, mem );
|
||||
SET_VTABLE_ENTRY( vtable, stream_getPos, mem );
|
||||
|
|
|
@ -74,7 +74,7 @@ static void setPendingCounts( ModelCtxt* model, XP_S16 turn );
|
|||
static void loadPlayerCtxt( const ModelCtxt* model, XWStreamCtxt* stream,
|
||||
XP_U16 version, PlayerCtxt* pc );
|
||||
static void writePlayerCtxt( const ModelCtxt* model, XWStreamCtxt* stream,
|
||||
PlayerCtxt* pc );
|
||||
const PlayerCtxt* pc );
|
||||
static XP_U16 model_getRecentPassCount( ModelCtxt* model );
|
||||
static XP_Bool recordWord( const XP_UCHAR* word, XP_Bool isLegal,
|
||||
#ifdef XWFEATURE_BOARDWORDS
|
||||
|
@ -179,7 +179,7 @@ model_makeFromStream( MPFORMAL XWStreamCtxt* stream, DictionaryCtxt* dict,
|
|||
} /* model_makeFromStream */
|
||||
|
||||
void
|
||||
model_writeToStream( ModelCtxt* model, XWStreamCtxt* stream )
|
||||
model_writeToStream( const ModelCtxt* model, XWStreamCtxt* stream )
|
||||
{
|
||||
XP_U16 ii;
|
||||
#ifdef STREAM_VERS_BIGBOARD
|
||||
|
@ -2376,10 +2376,11 @@ loadPlayerCtxt( const ModelCtxt* model, XWStreamCtxt* stream, XP_U16 version,
|
|||
} /* loadPlayerCtxt */
|
||||
|
||||
static void
|
||||
writePlayerCtxt( const ModelCtxt* model, XWStreamCtxt* stream, PlayerCtxt* pc )
|
||||
writePlayerCtxt( const ModelCtxt* model, XWStreamCtxt* stream,
|
||||
const PlayerCtxt* pc )
|
||||
{
|
||||
XP_U16 nTiles;
|
||||
PendingTile* pt;
|
||||
const PendingTile* pt;
|
||||
XP_U16 nColsNBits;
|
||||
#ifdef STREAM_VERS_BIGBOARD
|
||||
nColsNBits = 16 <= model_numCols( model ) ? NUMCOLS_NBITS_5
|
||||
|
|
|
@ -106,7 +106,7 @@ ModelCtxt* model_makeFromStream( MPFORMAL XWStreamCtxt* stream,
|
|||
DictionaryCtxt* dict, const PlayerDicts* dicts,
|
||||
XW_UtilCtxt* util );
|
||||
|
||||
void model_writeToStream( ModelCtxt* model, XWStreamCtxt* stream );
|
||||
void model_writeToStream( const ModelCtxt* model, XWStreamCtxt* stream );
|
||||
|
||||
#ifdef TEXT_MODEL
|
||||
void model_writeToTextStream( const ModelCtxt* model, XWStreamCtxt* stream );
|
||||
|
|
|
@ -100,7 +100,7 @@ stack_loadFromStream( StackCtxt* stack, XWStreamCtxt* stream )
|
|||
NULL, 0,
|
||||
(MemStreamCloseCallback)NULL );
|
||||
|
||||
stream_copyFromStream( stack->data, stream, nBytes );
|
||||
stream_getFromStream( stack->data, stream, nBytes );
|
||||
} else {
|
||||
XP_ASSERT( stack->nEntries == 0 );
|
||||
XP_ASSERT( stack->top == 0 );
|
||||
|
@ -115,7 +115,7 @@ stack_writeToStream( const StackCtxt* stack, XWStreamCtxt* stream )
|
|||
XWStreamPos oldPos = START_OF_STREAM;
|
||||
|
||||
if ( !!data ) {
|
||||
oldPos = stream_setPos( data, START_OF_STREAM, POS_READ );
|
||||
oldPos = stream_setPos( data, POS_READ, START_OF_STREAM );
|
||||
nBytes = stream_getSize( data );
|
||||
} else {
|
||||
nBytes = 0;
|
||||
|
@ -128,13 +128,9 @@ stack_writeToStream( const StackCtxt* stack, XWStreamCtxt* stream )
|
|||
stream_putU16( stream, stack->nEntries );
|
||||
stream_putU32( stream, stack->top );
|
||||
|
||||
stream_setPos( data, START_OF_STREAM, POS_READ );
|
||||
stream_copyFromStream( stream, data, nBytes );
|
||||
}
|
||||
|
||||
if ( !!data ) {
|
||||
stream_getFromStream( stream, data, nBytes );
|
||||
/* in case it'll be used further */
|
||||
(void)stream_setPos( data, oldPos, POS_READ );
|
||||
(void)stream_setPos( data, POS_READ, oldPos );
|
||||
}
|
||||
} /* stack_writeToStream */
|
||||
|
||||
|
@ -167,7 +163,7 @@ pushEntry( StackCtxt* stack, const StackEntry* entry )
|
|||
stack->data = stream;
|
||||
}
|
||||
|
||||
oldLoc = stream_setPos( stream, stack->top, POS_WRITE );
|
||||
oldLoc = stream_setPos( stream, POS_WRITE, stack->top );
|
||||
|
||||
stream_putBits( stream, 2, entry->moveType );
|
||||
stream_putBits( stream, 2, entry->playerNum );
|
||||
|
@ -211,7 +207,7 @@ pushEntry( StackCtxt* stack, const StackEntry* entry )
|
|||
|
||||
++stack->nEntries;
|
||||
stack->highWaterMark = stack->nEntries;
|
||||
stack->top = stream_setPos( stream, oldLoc, POS_WRITE );
|
||||
stack->top = stream_setPos( stream, POS_WRITE, oldLoc );
|
||||
} /* pushEntry */
|
||||
|
||||
static void
|
||||
|
@ -280,7 +276,7 @@ stack_addMove( StackCtxt* stack, XP_U16 turn, const MoveInfo* moveInfo,
|
|||
} /* stack_addMove */
|
||||
|
||||
void
|
||||
stack_addPhony( StackCtxt* stack, XP_U16 turn, MoveInfo* moveInfo )
|
||||
stack_addPhony( StackCtxt* stack, XP_U16 turn, const MoveInfo* moveInfo )
|
||||
{
|
||||
StackEntry move;
|
||||
|
||||
|
@ -327,7 +323,7 @@ setCacheReadyFor( StackCtxt* stack, XP_U16 n )
|
|||
StackEntry dummy;
|
||||
XP_U16 i;
|
||||
|
||||
stream_setPos( stack->data, START_OF_STREAM, POS_READ );
|
||||
stream_setPos( stack->data, POS_READ, START_OF_STREAM );
|
||||
for ( i = 0; i < n; ++i ) {
|
||||
readEntry( stack, &dummy );
|
||||
}
|
||||
|
@ -339,34 +335,34 @@ setCacheReadyFor( StackCtxt* stack, XP_U16 n )
|
|||
} /* setCacheReadyFor */
|
||||
|
||||
XP_U16
|
||||
stack_getNEntries( StackCtxt* stack )
|
||||
stack_getNEntries( const StackCtxt* stack )
|
||||
{
|
||||
return stack->nEntries;
|
||||
} /* stack_getNEntries */
|
||||
|
||||
XP_Bool
|
||||
stack_getNthEntry( StackCtxt* stack, XP_U16 n, StackEntry* entry )
|
||||
stack_getNthEntry( StackCtxt* stack, XP_U16 nn, StackEntry* entry )
|
||||
{
|
||||
XP_Bool found;
|
||||
|
||||
if ( n >= stack->nEntries ) {
|
||||
if ( nn >= stack->nEntries ) {
|
||||
found = XP_FALSE;
|
||||
} else if ( stack->cacheNext != n ) {
|
||||
} else if ( stack->cacheNext != nn ) {
|
||||
XP_ASSERT( !!stack->data );
|
||||
found = setCacheReadyFor( stack, n );
|
||||
XP_ASSERT( stack->cacheNext == n );
|
||||
found = setCacheReadyFor( stack, nn );
|
||||
XP_ASSERT( stack->cacheNext == nn );
|
||||
} else {
|
||||
found = XP_TRUE;
|
||||
}
|
||||
|
||||
if ( found ) {
|
||||
XWStreamPos oldPos = stream_setPos( stack->data, stack->cachedPos,
|
||||
POS_READ );
|
||||
XWStreamPos oldPos = stream_setPos( stack->data, POS_READ,
|
||||
stack->cachedPos );
|
||||
|
||||
readEntry( stack, entry );
|
||||
entry->moveNum = (XP_U8)n;
|
||||
entry->moveNum = (XP_U8)nn;
|
||||
|
||||
stack->cachedPos = stream_setPos( stack->data, oldPos, POS_READ );
|
||||
stack->cachedPos = stream_setPos( stack->data, POS_READ, oldPos );
|
||||
++stack->cacheNext;
|
||||
}
|
||||
|
||||
|
|
|
@ -77,14 +77,14 @@ StackCtxt* stack_copy( const StackCtxt* stack );
|
|||
|
||||
void stack_addMove( StackCtxt* stack, XP_U16 turn, const MoveInfo* moveInfo,
|
||||
const TrayTileSet* newTiles );
|
||||
void stack_addPhony( StackCtxt* stack, XP_U16 turn, MoveInfo* moveInfo );
|
||||
void stack_addPhony( StackCtxt* stack, XP_U16 turn, const MoveInfo* moveInfo );
|
||||
void stack_addTrade( StackCtxt* stack, XP_U16 turn,
|
||||
const TrayTileSet* oldTiles,
|
||||
const TrayTileSet* newTiles );
|
||||
void stack_addAssign( StackCtxt* stack, XP_U16 turn,
|
||||
const TrayTileSet* tiles );
|
||||
|
||||
XP_U16 stack_getNEntries( StackCtxt* stack );
|
||||
XP_U16 stack_getNEntries( const StackCtxt* stack );
|
||||
|
||||
XP_Bool stack_getNthEntry( StackCtxt* stack, XP_U16 n, StackEntry* entry );
|
||||
|
||||
|
|
|
@ -305,7 +305,7 @@ getNV( XWStreamCtxt* stream, ServerNonvolatiles* nv, XP_U16 nPlayers )
|
|||
} /* getNV */
|
||||
|
||||
static void
|
||||
putNV( XWStreamCtxt* stream, ServerNonvolatiles* nv, XP_U16 nPlayers )
|
||||
putNV( XWStreamCtxt* stream, const ServerNonvolatiles* nv, XP_U16 nPlayers )
|
||||
{
|
||||
XP_U16 ii;
|
||||
|
||||
|
@ -339,7 +339,7 @@ readStreamIf( ServerCtxt* server, XWStreamCtxt* in )
|
|||
XP_U16 len = stream_getU16( in );
|
||||
if ( 0 < len ) {
|
||||
result = mkServerStream( server );
|
||||
stream_copyFromStream( result, in, len );
|
||||
stream_getFromStream( result, in, len );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -350,7 +350,9 @@ writeStreamIf( XWStreamCtxt* dest, XWStreamCtxt* src )
|
|||
XP_U16 len = !!src ? stream_getSize( src ) : 0;
|
||||
stream_putU16( dest, len );
|
||||
if ( 0 < len ) {
|
||||
stream_copyFromStream( dest, src, len );
|
||||
XWStreamPos pos = stream_getPos( src, POS_READ );
|
||||
stream_getFromStream( dest, src, len );
|
||||
(void)stream_setPos( src, POS_READ, pos );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -402,7 +404,7 @@ server_makeFromStream( MPFORMAL XWStreamCtxt* stream, ModelCtxt* model,
|
|||
} /* server_makeFromStream */
|
||||
|
||||
void
|
||||
server_writeToStream( ServerCtxt* server, XWStreamCtxt* stream )
|
||||
server_writeToStream( const ServerCtxt* server, XWStreamCtxt* stream )
|
||||
{
|
||||
XP_U16 i;
|
||||
XP_U16 nPlayers = server->vol.gi->nPlayers;
|
||||
|
@ -415,7 +417,7 @@ server_writeToStream( ServerCtxt* server, XWStreamCtxt* stream )
|
|||
}
|
||||
|
||||
for ( i = 0; i < nPlayers; ++i ) {
|
||||
ServerPlayer* player = &server->players[i];
|
||||
const ServerPlayer* player = &server->players[i];
|
||||
|
||||
stream_putU8( stream, player->deviceIndex );
|
||||
|
||||
|
@ -433,7 +435,6 @@ server_writeToStream( ServerCtxt* server, XWStreamCtxt* stream )
|
|||
|
||||
writeStreamIf( stream, server->nv.prevMoveStream );
|
||||
writeStreamIf( stream, server->nv.prevWordsStream );
|
||||
|
||||
} /* server_writeToStream */
|
||||
|
||||
static void
|
||||
|
|
|
@ -62,7 +62,7 @@ ServerCtxt* server_makeFromStream( MPFORMAL XWStreamCtxt* stream,
|
|||
ModelCtxt* model, CommsCtxt* comms,
|
||||
XW_UtilCtxt* util, XP_U16 nPlayers );
|
||||
|
||||
void server_writeToStream( ServerCtxt* server, XWStreamCtxt* stream );
|
||||
void server_writeToStream( const ServerCtxt* server, XWStreamCtxt* stream );
|
||||
|
||||
void server_reset( ServerCtxt* server, CommsCtxt* comms );
|
||||
void server_destroy( ServerCtxt* server );
|
||||
|
|
|
@ -26,8 +26,8 @@
|
|||
|
||||
#define START_OF_STREAM 0
|
||||
#define END_OF_STREAM -1
|
||||
|
||||
typedef XP_U32 XWStreamPos; /* low 3 bits are bit offset; rest byte offset */
|
||||
|
||||
enum { POS_READ, POS_WRITE };
|
||||
typedef XP_U8 PosWhich;
|
||||
|
||||
|
@ -58,12 +58,12 @@ typedef struct StreamCtxVTable {
|
|||
void (*m_stream_putBits)( XWStreamCtxt* dctx, XP_U16 nBits, XP_U32 bits
|
||||
DBG_LINE_FILE_FORMAL );
|
||||
|
||||
void (*m_stream_copyFromStream)( XWStreamCtxt* dctx, XWStreamCtxt* src,
|
||||
void (*m_stream_getFromStream)( XWStreamCtxt* dctx, XWStreamCtxt* src,
|
||||
XP_U16 nBytes );
|
||||
|
||||
XWStreamPos (*m_stream_getPos)( XWStreamCtxt* dctx, PosWhich which );
|
||||
XWStreamPos (*m_stream_setPos)( XWStreamCtxt* dctx, XWStreamPos newpos,
|
||||
PosWhich which );
|
||||
XWStreamPos (*m_stream_getPos)( const XWStreamCtxt* dctx, PosWhich which );
|
||||
XWStreamPos (*m_stream_setPos)( XWStreamCtxt* dctx, PosWhich which,
|
||||
XWStreamPos newpos );
|
||||
|
||||
void (*m_stream_open)( XWStreamCtxt* dctx );
|
||||
void (*m_stream_close)( XWStreamCtxt* dctx );
|
||||
|
@ -75,11 +75,11 @@ typedef struct StreamCtxVTable {
|
|||
/* void (*m_stream_makeReturnAddr)( XWStreamCtxt* dctx, XP_PlayerAddr* addr, */
|
||||
/* XP_U16* addrLen ); */
|
||||
|
||||
XP_PlayerAddr (*m_stream_getAddress)( XWStreamCtxt* dctx );
|
||||
XP_PlayerAddr (*m_stream_getAddress)( const XWStreamCtxt* dctx );
|
||||
void (*m_stream_setAddress)( XWStreamCtxt* dctx, XP_PlayerAddr channelNo );
|
||||
|
||||
void (*m_stream_setVersion)( XWStreamCtxt* dctx, XP_U16 vers );
|
||||
XP_U16 (*m_stream_getVersion)( XWStreamCtxt* dctx );
|
||||
XP_U16 (*m_stream_getVersion)( const XWStreamCtxt* dctx );
|
||||
|
||||
void (*m_stream_setOnCloseProc)( XWStreamCtxt* dctx,
|
||||
MemStreamCloseCallback proc );
|
||||
|
@ -127,8 +127,8 @@ struct XWStreamCtxt {
|
|||
#define stream_putBits(sc, n, b) \
|
||||
(sc)->vtable->m_stream_putBits((sc), (n), (b) DBG_LINE_FILE_PARM )
|
||||
|
||||
#define stream_copyFromStream( sc, src, nb ) \
|
||||
(sc)->vtable->m_stream_copyFromStream((sc), (src), (nb))
|
||||
#define stream_getFromStream( sc, src, nb ) \
|
||||
(sc)->vtable->m_stream_getFromStream((sc), (src), (nb))
|
||||
|
||||
#define stream_getPos(sc, w) \
|
||||
(sc)->vtable->m_stream_getPos((sc), (w))
|
||||
|
|
|
@ -182,7 +182,11 @@ OBJ = \
|
|||
$(PLATFORM)/linuxutl.o \
|
||||
$(CURSES_OBJS) $(GTK_OBJS) $(MAIN_OBJS)
|
||||
|
||||
LIBS = -lm -lmcheck -luuid $(GPROFFLAG)
|
||||
LIBS = -lm -luuid $(GPROFFLAG)
|
||||
# Turn this off for now. I apparently have a memory problem, but it
|
||||
# doesn't make the app unusable for testing while crash on boot does.
|
||||
# LIBS += -lmcheck
|
||||
|
||||
ifdef BLUETOOTH
|
||||
LIBS += -lbluetooth
|
||||
endif
|
||||
|
|
|
@ -1896,6 +1896,7 @@ makeVerticalBar( GtkAppGlobals* globals, GtkWidget* XP_UNUSED(window) )
|
|||
gtk_box_pack_start( GTK_BOX(vbox), button, FALSE, TRUE, 0 );
|
||||
button = makeShowButtonFromBitmap( globals, "../done.xpm", "-",
|
||||
G_CALLBACK(handle_zoomout_button) );
|
||||
globals->zoomout_button = button;
|
||||
gtk_box_pack_start( GTK_BOX(vbox), button, FALSE, TRUE, 0 );
|
||||
#ifdef XWFEATURE_CHAT
|
||||
button = makeShowButtonFromBitmap( globals, "", "chat",
|
||||
|
|
Loading…
Reference in a new issue