mirror of
git://xwords.git.sourceforge.net/gitroot/xwords/xwords
synced 2024-12-30 10:26:58 +01:00
51b0fd5e4e
after loading prefs since that's where language pref will be stored. But for now, hard-code to load French so translator can see it immediately. Also, create l10n directory in which localized .rc files will live and modify Make file to .dlls from all.
3571 lines
110 KiB
C
Executable file
3571 lines
110 KiB
C
Executable file
/* -*- fill-column: 77; compile-command: "make -j2 TARGET_OS=wince DEBUG=TRUE" -*- */
|
|
/*
|
|
* Copyright 2002-2009 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.
|
|
*
|
|
* Derived from code generated by M$'s eVC++.
|
|
*/
|
|
|
|
#include "stdafx.h"
|
|
#include "xwords4.h"
|
|
#include <commctrl.h>
|
|
#include <commdlg.h>
|
|
#include <stdio.h>
|
|
#include <time.h> /* time() */
|
|
#include <winuser.h>
|
|
#ifdef _WIN32_WCE
|
|
# include <aygshell.h>
|
|
#endif
|
|
/* #include <imm.h> */
|
|
#include "strutils.h"
|
|
|
|
#include "memstream.h"
|
|
|
|
#include "cemain.h"
|
|
#include "cedefines.h"
|
|
|
|
#include "ceginfo.h"
|
|
#include "cestrbx.h"
|
|
#include "cedict.h"
|
|
#include "ceblank.h"
|
|
#include "ceprefs.h"
|
|
#include "ceaskpwd.h"
|
|
#include "ceutil.h"
|
|
#include "ceir.h"
|
|
#include "ceclrsel.h"
|
|
#include "cehntlim.h"
|
|
#include "cedebug.h"
|
|
#include "LocalizedStrIncludes.h"
|
|
#include "debhacks.h"
|
|
#include "cesvdgms.h"
|
|
#include "cedraw.h"
|
|
#include "cesms.h"
|
|
#include "cesockwr.h"
|
|
#include "ceresstr.h"
|
|
|
|
#include "dbgutil.h"
|
|
|
|
#define MAX_LOADSTRING 100
|
|
|
|
#define MAX_SCROLLBAR_WIDTH 12
|
|
#define MIN_SCROLLBAR_WIDTH 6
|
|
#define SCROLLBARID 0x4321 /* needs to be unique! */
|
|
|
|
#ifdef MEM_DEBUG
|
|
# define MEMPOOL globals->mpool,
|
|
#else
|
|
# define MEMPOOL
|
|
#endif
|
|
|
|
typedef struct FileWriteState {
|
|
CEAppGlobals* globals;
|
|
XP_UCHAR* path;
|
|
} FileWriteState;
|
|
|
|
/* forward util function decls */
|
|
#ifndef XWFEATURE_STANDALONE_ONLY
|
|
static XP_S16 ce_send_proc( const XP_U8* buf, XP_U16 len,
|
|
const CommsAddrRec* addr,
|
|
void* closure );
|
|
|
|
#define CE_SEND_PROC ce_send_proc
|
|
#else
|
|
#define CE_SEND_PROC NULL
|
|
#endif
|
|
|
|
#ifdef COMMS_HEARTBEAT
|
|
static void ce_reset_proc( void* closure );
|
|
# define CE_RESET_PROC ce_reset_proc,
|
|
#else
|
|
# define CE_RESET_PROC
|
|
#endif
|
|
|
|
static VTableMgr* ce_util_getVTManager( XW_UtilCtxt* uc );
|
|
static void ce_util_userError( XW_UtilCtxt* uc, UtilErrID id );
|
|
static XP_Bool ce_util_userQuery( XW_UtilCtxt* uc, UtilQueryID id,
|
|
XWStreamCtxt* stream );
|
|
static XWBonusType ce_util_getSquareBonus( XW_UtilCtxt* uc,
|
|
const ModelCtxt* model,
|
|
XP_U16 col, XP_U16 row );
|
|
static XP_S16 ce_util_userPickTile( XW_UtilCtxt* uc, const PickInfo* pi,
|
|
XP_U16 playerNum,
|
|
const XP_UCHAR** texts, XP_U16 nTiles );
|
|
static XP_Bool ce_util_askPassword( XW_UtilCtxt* uc, const XP_UCHAR* name,
|
|
XP_UCHAR* buf, XP_U16* len );
|
|
static void ce_util_trayHiddenChange( XW_UtilCtxt* uc,
|
|
XW_TrayVisState newState,
|
|
XP_U16 nVisibleRows );
|
|
static void ce_util_yOffsetChange( XW_UtilCtxt* uc, XP_U16 oldOffset,
|
|
XP_U16 newOffset );
|
|
static void ce_util_turnChanged( XW_UtilCtxt* uc );
|
|
static void ce_util_notifyGameOver( XW_UtilCtxt* uc );
|
|
static XP_Bool ce_util_hiliteCell( XW_UtilCtxt* uc, XP_U16 col,
|
|
XP_U16 row );
|
|
static XP_Bool ce_util_engineProgressCallback( XW_UtilCtxt* uc );
|
|
static void ce_util_setTimer( XW_UtilCtxt* uc, XWTimerReason why, XP_U16 when,
|
|
XWTimerProc proc, void* closure);
|
|
static XP_Bool ce_util_altKeyDown( XW_UtilCtxt* uc );
|
|
static void ce_util_requestTime( XW_UtilCtxt* uc );
|
|
static XP_U32 ce_util_getCurSeconds( XW_UtilCtxt* uc );
|
|
static DictionaryCtxt* ce_util_makeEmptyDict( XW_UtilCtxt* uc );
|
|
#ifdef XWFEATURE_RELAY
|
|
static XWStreamCtxt* ce_util_makeStreamFromAddr( XW_UtilCtxt* uc,
|
|
XP_PlayerAddr channelNo );
|
|
#endif
|
|
static const XP_UCHAR* ce_util_getUserString( XW_UtilCtxt* uc,
|
|
XP_U16 stringCode );
|
|
static XP_Bool ce_util_warnIllegalWord( XW_UtilCtxt* uc, BadWordInfo* bwi,
|
|
XP_U16 turn, XP_Bool turnLost );
|
|
static void ce_util_remSelected( XW_UtilCtxt* uc );
|
|
#ifndef XWFEATURE_STANDALONE_ONLY
|
|
static void ce_util_addrChange( XW_UtilCtxt* uc, const CommsAddrRec* oldAddr,
|
|
const CommsAddrRec* newAddr );
|
|
#endif
|
|
|
|
#ifdef XWFEATURE_SEARCHLIMIT
|
|
static XP_Bool ce_util_getTraySearchLimits( XW_UtilCtxt* uc, XP_U16* min,
|
|
XP_U16* max );
|
|
#endif
|
|
#ifdef SHOW_PROGRESS
|
|
static void ce_util_engineStarting( XW_UtilCtxt* uc );
|
|
static void ce_util_engineStopping( XW_UtilCtxt* uc );
|
|
#endif
|
|
|
|
static XP_Bool ceMsgFromStream( CEAppGlobals* globals, XWStreamCtxt* stream,
|
|
const wchar_t* title, XP_U16 buttons,
|
|
XP_Bool destroy );
|
|
static void RECTtoXPR( XP_Rect* dest, const RECT* src );
|
|
static XP_Bool ceDoNewGame( CEAppGlobals* globals );
|
|
static XP_Bool ceSaveCurGame( CEAppGlobals* globals, XP_Bool autoSave );
|
|
static void closeGame( CEAppGlobals* globals );
|
|
static void ceInitPrefs( CEAppGlobals* globals, CEAppPrefs* prefs );
|
|
static void updateForColors( CEAppGlobals* globals );
|
|
static XWStreamCtxt* make_generic_stream( const CEAppGlobals* globals );
|
|
#ifdef XWFEATURE_RELAY
|
|
static void ce_send_on_close( XWStreamCtxt* stream, void* closure );
|
|
#endif
|
|
static XP_Bool ceSetDictName( const wchar_t* wPath, XP_U16 index, void* ctxt );
|
|
static int messageBoxStream( CEAppGlobals* globals, XWStreamCtxt* stream,
|
|
const wchar_t* title, XP_U16 buttons );
|
|
static XP_Bool ceQueryFromStream( CEAppGlobals* globals, XWStreamCtxt* stream);
|
|
static XP_Bool isDefaultName( CEAppGlobals* globals, const XP_UCHAR* name );
|
|
static void ceSetTitleFromName( CEAppGlobals* globals );
|
|
static void removeScrollbar( CEAppGlobals* globals );
|
|
|
|
|
|
#ifndef _WIN32_WCE
|
|
/* Very basic cmdline args meant at first to let me vary the size of the
|
|
* screen. Form is of arg=digits, with no spaces and digits having to be an
|
|
* integer. Right now only width and height work: e.g.
|
|
* "wine obj_win32_dbg/xwords4.exe height=240 width=320"
|
|
*/
|
|
static XP_Bool
|
|
tryIntParam( const char* buf, const char* param, XP_U16* value )
|
|
{
|
|
XP_U16 len = strlen( param );
|
|
XP_Bool found = 0 == strncmp( buf, param, len );
|
|
if ( found ) {
|
|
*value = atoi( &buf[len] );
|
|
}
|
|
return found;
|
|
}
|
|
|
|
static XP_Bool
|
|
tryCharParam( const char* buf, const char* param, char* out )
|
|
{
|
|
XP_U16 len = strlen( param );
|
|
XP_Bool found = 0 == strncmp( buf, param, len );
|
|
if ( found ) {
|
|
int ii;
|
|
for ( ii = len; buf[ii] != ' ' && buf[ii] != 0 ; ++ii ) {
|
|
}
|
|
memcpy( out, &buf[len], ii - len );
|
|
out[ii-len] = 0;
|
|
XP_LOGF( "%s: \"%s\"", __func__, out );
|
|
}
|
|
return found;
|
|
}
|
|
|
|
static void
|
|
parseCmdLine( const char* cmdline, XP_U16* width, XP_U16* height,
|
|
char* dll )
|
|
{
|
|
XP_U16 ii;
|
|
for ( ii = 0; ; ++ii ) {
|
|
const char* cmd;
|
|
char ch;
|
|
char buf[64];
|
|
int len;
|
|
for ( cmd = cmdline ; ; ++cmd ) {
|
|
ch = *cmd;
|
|
if ( ch == '\0' || ch == ' ' ) {
|
|
break;
|
|
}
|
|
}
|
|
len = cmd - cmdline;
|
|
if ( len < sizeof(buf) ) {
|
|
memcpy( buf, cmdline, cmd - cmdline );
|
|
buf[len] = '\0';
|
|
if ( ii > 0 ) { /* skip argv[0] */
|
|
if ( tryIntParam( buf, "width=", width ) ) {
|
|
} else if ( tryIntParam( buf, "height=", height ) ) {
|
|
} else if ( tryCharParam( buf, "dll=", dll ) ) {
|
|
} else {
|
|
XP_LOGF( "failed to match cmdline arg \"%s\"", buf );
|
|
}
|
|
}
|
|
}
|
|
if ( ch == '\0' ) {
|
|
break;
|
|
}
|
|
cmdline = ++cmd;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Forward declarations of functions included in this code module:
|
|
ATOM MyRegisterClass (HINSTANCE, LPTSTR);
|
|
BOOL InitInstance (HINSTANCE, int
|
|
#ifndef _WIN32_WCE
|
|
, XP_U16, XP_U16, const char*
|
|
#endif
|
|
);
|
|
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
|
|
LRESULT CALLBACK ceAbout (HWND, UINT, WPARAM, LPARAM);
|
|
|
|
int WINAPI
|
|
WinMain( HINSTANCE hInstance,
|
|
HINSTANCE XP_UNUSED(hPrevInstance),
|
|
#ifdef _WIN32_WCE
|
|
LPWSTR XP_UNUSED_CE(lpCmdLine),
|
|
#else
|
|
LPSTR lpCmdLine,
|
|
#endif
|
|
int nCmdShow)
|
|
{
|
|
MSG msg;
|
|
HACCEL hAccelTable;
|
|
|
|
#ifndef _WIN32_WCE
|
|
XP_U16 width = 320, height = 320;
|
|
char dll[MAX_PATH];
|
|
parseCmdLine( lpCmdLine, &width, &height, dll );
|
|
#endif
|
|
|
|
// Perform application initialization:
|
|
if (!InitInstance (hInstance, nCmdShow
|
|
#ifndef _WIN32_WCE
|
|
, width, height, dll
|
|
#endif
|
|
)) {
|
|
return FALSE;
|
|
}
|
|
|
|
hAccelTable = LoadAccelerators(hInstance, (LPCTSTR)IDC_XWORDS4);
|
|
|
|
// Main message loop. Return of 0 indicates quit message. Return of -1
|
|
// indicates major error (so we just bail.)
|
|
while ( 0 < GetMessage(&msg, NULL, 0, 0) ) {
|
|
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) {
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
}
|
|
|
|
/* This would be a good place to free up memory, close sockets, etc. */
|
|
|
|
LOG_RETURNF( "%d", msg.wParam );
|
|
return msg.wParam;
|
|
}
|
|
|
|
#ifdef __GNUC__
|
|
int
|
|
main()
|
|
{
|
|
LOG_FUNC();
|
|
|
|
return WinMain( GetModuleHandle(NULL), 0,
|
|
#ifdef _WIN32_WCE
|
|
GetCommandLineW(),
|
|
#else
|
|
GetCommandLineA(),
|
|
#endif
|
|
SW_SHOWDEFAULT );
|
|
}
|
|
#endif
|
|
|
|
|
|
//
|
|
// FUNCTION: MyRegisterClass()
|
|
//
|
|
// PURPOSE: Registers the window class.
|
|
//
|
|
// COMMENTS:
|
|
//
|
|
// It is important to call this function so that the application
|
|
// will get 'well formed' small icons associated with it.
|
|
//
|
|
ATOM
|
|
MyRegisterClass(HINSTANCE hInstance, LPTSTR szWindowClass)
|
|
{
|
|
WNDCLASS wc;
|
|
|
|
XP_MEMSET( &wc, 0, sizeof(wc) );
|
|
|
|
wc.style = CS_HREDRAW | CS_VREDRAW;
|
|
wc.lpfnWndProc = (WNDPROC) WndProc;
|
|
wc.cbWndExtra = sizeof(CEAppGlobals*);
|
|
wc.hInstance = hInstance;
|
|
wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_XWORDS4));
|
|
wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
|
|
wc.lpszClassName = szWindowClass;
|
|
|
|
return RegisterClass(&wc);
|
|
}
|
|
|
|
static void
|
|
ceInitUtilFuncs( CEAppGlobals* globals )
|
|
{
|
|
UtilVtable* vtable = globals->util.vtable =
|
|
XP_MALLOC( globals->mpool, sizeof( UtilVtable ) );
|
|
globals->util.closure = (void*)globals;
|
|
globals->util.gameInfo = &globals->gameInfo;
|
|
|
|
MPASSIGN( globals->util.mpool, globals->mpool );
|
|
|
|
vtable->m_util_getVTManager = ce_util_getVTManager;
|
|
vtable->m_util_userError = ce_util_userError;
|
|
vtable->m_util_getSquareBonus = ce_util_getSquareBonus;
|
|
vtable->m_util_userQuery = ce_util_userQuery;
|
|
vtable->m_util_userPickTile = ce_util_userPickTile;
|
|
vtable->m_util_askPassword = ce_util_askPassword;
|
|
vtable->m_util_trayHiddenChange = ce_util_trayHiddenChange;
|
|
vtable->m_util_yOffsetChange = ce_util_yOffsetChange;
|
|
vtable->m_util_turnChanged = ce_util_turnChanged;
|
|
vtable->m_util_notifyGameOver = ce_util_notifyGameOver;
|
|
vtable->m_util_hiliteCell = ce_util_hiliteCell;
|
|
vtable->m_util_engineProgressCallback = ce_util_engineProgressCallback;
|
|
vtable->m_util_setTimer = ce_util_setTimer;
|
|
vtable->m_util_altKeyDown = ce_util_altKeyDown;
|
|
vtable->m_util_requestTime = ce_util_requestTime;
|
|
vtable->m_util_getCurSeconds = ce_util_getCurSeconds;
|
|
vtable->m_util_makeEmptyDict = ce_util_makeEmptyDict;
|
|
vtable->m_util_getUserString = ce_util_getUserString;
|
|
vtable->m_util_warnIllegalWord = ce_util_warnIllegalWord;
|
|
vtable->m_util_remSelected = ce_util_remSelected;
|
|
#ifdef XWFEATURE_RELAY
|
|
vtable->m_util_addrChange = ce_util_addrChange;
|
|
vtable->m_util_makeStreamFromAddr = ce_util_makeStreamFromAddr;
|
|
#endif
|
|
#ifdef XWFEATURE_SEARCHLIMIT
|
|
vtable->m_util_getTraySearchLimits = ce_util_getTraySearchLimits;
|
|
#endif
|
|
#ifdef SHOW_PROGRESS
|
|
vtable->m_util_engineStarting = ce_util_engineStarting;
|
|
vtable->m_util_engineStopping = ce_util_engineStopping;
|
|
#endif
|
|
|
|
} /* ceInitUtilFuncs */
|
|
|
|
#ifdef CEFEATURE_CANSCROLL
|
|
# define SCROLL_SHRINK 1
|
|
|
|
static void
|
|
updateScrollInfo( CEAppGlobals* globals, XP_U16 nHidden )
|
|
{
|
|
SCROLLINFO sinfo;
|
|
|
|
XP_MEMSET( &sinfo, 0, sizeof(sinfo) );
|
|
sinfo.cbSize = sizeof(sinfo);
|
|
sinfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
|
|
sinfo.nMax = model_numRows( globals->game.model );
|
|
sinfo.nPage = sinfo.nMax - nHidden + 1;
|
|
|
|
(void)SetScrollInfo( globals->scrollHandle, SB_CTL, &sinfo, TRUE );
|
|
}
|
|
|
|
LRESULT CALLBACK
|
|
scrollWindowProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
|
|
{
|
|
CEAppGlobals* globals = (CEAppGlobals*)GetWindowLongPtr( hWnd, GWL_USERDATA );
|
|
LRESULT result = 1;
|
|
|
|
/* XP_LOGF( "%s: event=%s (%d)", __func__, messageToStr(message), message ); */
|
|
|
|
/* Trap key events. Left and right always shift the focus off. Up and
|
|
down shift focus off IFF they're going to be no-ops on the theory that
|
|
the user needs to get some visual feedback and on some devices the
|
|
scrollbar isn't even drawn differently when focussed. */
|
|
if ( WM_KEYDOWN == message ) {
|
|
XP_Bool setFocus = XP_FALSE;
|
|
|
|
if ( (VK_RIGHT == wParam) ||
|
|
(VK_LEFT == wParam) ||
|
|
(VK_TAB == wParam) ) {
|
|
setFocus = XP_TRUE;
|
|
} else if ( (VK_UP == wParam) || (VK_DOWN == wParam) ) {
|
|
SCROLLINFO sinfo;
|
|
sinfo.cbSize = sizeof(sinfo);
|
|
sinfo.fMask = SIF_POS | SIF_RANGE | SIF_PAGE;
|
|
GetScrollInfo( globals->scrollHandle, SB_CTL, &sinfo );
|
|
|
|
if ( (VK_UP == wParam) && (sinfo.nPos <= sinfo.nMin) ) {
|
|
setFocus = XP_TRUE;
|
|
} else if ( (VK_DOWN == wParam)
|
|
&& (sinfo.nPos > (sinfo.nMax - sinfo.nPage)) ) {
|
|
setFocus = XP_TRUE;
|
|
}
|
|
}
|
|
|
|
if ( setFocus ) {
|
|
SetFocus( globals->hWnd );
|
|
result = 0;
|
|
globals->keyDown = XP_TRUE;
|
|
}
|
|
}
|
|
if ( 0 != result ) {
|
|
result = CallWindowProc( globals->oldScrollProc, hWnd, message,
|
|
wParam, lParam );
|
|
}
|
|
return result;
|
|
} /* scrollWindowProc */
|
|
|
|
static void
|
|
makeScrollbar( CEAppGlobals* globals, XP_U16 nHidden, XP_U16 xx, XP_U16 yy,
|
|
XP_U16 width, XP_U16 height )
|
|
{
|
|
HWND hwndSB;
|
|
#ifdef _WIN32_WCE
|
|
RECT tmp;
|
|
XP_U16 rectHt;
|
|
#endif
|
|
|
|
++xx;
|
|
width -= 2; /* make narrower: on CE will erase cell border */
|
|
|
|
#ifdef _WIN32_WCE
|
|
tmp.left = xx;
|
|
tmp.right = xx + width;
|
|
rectHt = height / 10; /* each focus rect to be 1/10th height */
|
|
|
|
tmp.top = yy;
|
|
tmp.bottom = yy + rectHt;
|
|
XP_MEMCPY( &globals->scrollRects[0], &tmp, sizeof(globals->scrollRects[0]) );
|
|
|
|
tmp.bottom = yy + height;
|
|
tmp.top = tmp.bottom - rectHt;
|
|
XP_MEMCPY( &globals->scrollRects[1], &tmp, sizeof(globals->scrollRects[1]) );
|
|
|
|
yy += rectHt;
|
|
height -= rectHt * 2; /* above and below */
|
|
#endif
|
|
|
|
/* Need to destroy it, or resize it, because board size may be changing
|
|
in case where portrait display's been flipped. */
|
|
removeScrollbar( globals );
|
|
|
|
hwndSB = CreateWindow( TEXT("SCROLLBAR"), // Class name
|
|
NULL, // Window text
|
|
// Window style
|
|
SBS_VERT|WS_VISIBLE|WS_CHILD,
|
|
xx, yy, width, height, globals->hWnd,
|
|
(HMENU)SCROLLBARID, // The control identifier
|
|
globals->hInst, // The instance handle
|
|
NULL ); // s'pposed to be NULL
|
|
|
|
globals->scrollHandle = hwndSB;
|
|
|
|
globals->oldScrollProc = (WNDPROC) GetWindowLongPtr( hwndSB,
|
|
GWL_WNDPROC );
|
|
XP_ASSERT( 0 == GetWindowLongPtr( hwndSB, GWL_USERDATA ) );
|
|
SetWindowLongPtr( hwndSB, GWL_WNDPROC, (LPARAM)scrollWindowProc );
|
|
SetWindowLongPtr( hwndSB, GWL_USERDATA, (LPARAM)globals );
|
|
|
|
updateScrollInfo( globals, nHidden );
|
|
EnableWindow( hwndSB, nHidden > 0 );
|
|
|
|
ShowWindow( globals->scrollHandle, SW_SHOW );
|
|
} /* makeScrollbar */
|
|
|
|
static void
|
|
removeScrollbar( CEAppGlobals* globals )
|
|
{
|
|
if ( !!globals->scrollHandle ) {
|
|
DestroyWindow( globals->scrollHandle );
|
|
globals->scrollHandle = NULL;
|
|
globals->scrollerHasFocus = XP_FALSE;
|
|
}
|
|
} /* removeScrollbar */
|
|
#endif
|
|
|
|
typedef struct CEBoardParms {
|
|
XP_U16 scrnWidth;
|
|
XP_U16 scrnHeight;
|
|
XP_U16 adjLeft;
|
|
XP_U16 adjTop;
|
|
|
|
XP_U16 boardHScale;
|
|
XP_U16 boardVScale;
|
|
XP_U16 boardTop;
|
|
XP_U16 trayTop;
|
|
|
|
XP_U16 trayHeight;
|
|
XP_U16 trayWidth;
|
|
|
|
XP_U16 timerLeft, timerTop, timerWidth, timerHeight;
|
|
|
|
XP_U16 boardLeft, trayLeft;
|
|
XP_U16 scoreWidth;
|
|
XP_U16 scoreHeight;
|
|
XP_U16 scrollWidth;
|
|
XP_Bool horiz;
|
|
#ifdef CEFEATURE_CANSCROLL
|
|
XP_Bool needsScroller;
|
|
#endif
|
|
} CEBoardParms;
|
|
|
|
static void
|
|
figureBoardParms( CEAppGlobals* globals, const XP_U16 nRows,
|
|
CEBoardParms* bparms )
|
|
{
|
|
RECT rc;
|
|
XP_U16 scrnWidth, scrnHeight;
|
|
XP_U16 trayHeight, scoreWidth, scoreHeight;
|
|
XP_U16 hScale, vScale, nVisibleRows;
|
|
XP_U16 tmp;
|
|
XP_Bool horiz;
|
|
XP_U16 scrollWidth = 0;
|
|
XP_U16 adjLeft, adjTop;
|
|
|
|
GetClientRect( globals->hWnd, &rc );
|
|
#ifndef _WIN32_WCE
|
|
# if defined FORCE_HEIGHT && defined FORCE_WIDTH
|
|
rc.right = rc.left + FORCE_WIDTH;
|
|
rc.bottom = rc.top + FORCE_HEIGHT;
|
|
# else
|
|
if ( !globals->appPrefs.fullScreen ) {
|
|
if ( globals->dbWidth != 0 ) {
|
|
rc.right = rc.left + globals->dbWidth;
|
|
}
|
|
if ( globals->dbHeight != 0 ) {
|
|
rc.bottom = rc.top + globals->dbHeight;
|
|
}
|
|
}
|
|
# endif
|
|
#endif /* #ifndef _WIN32_WCE */
|
|
|
|
scrnWidth = (XP_U16)(rc.right - rc.left);
|
|
scrnHeight = (XP_U16)(rc.bottom - rc.top);
|
|
|
|
XP_LOGF( "%s: scrnWidth: %d, scrnHeight: %d", __func__,
|
|
scrnWidth, scrnHeight );
|
|
|
|
/* Figure layout style based on proportion UNLESS there's just no room
|
|
for anything but 15 columns on the board -- because vertically is the
|
|
only dimension in which we can scroll. */
|
|
if ( scrnWidth <= (MIN_CELL_WIDTH * (nRows+2)) ) {
|
|
horiz = XP_TRUE;
|
|
} else {
|
|
horiz = scrnHeight >= scrnWidth;
|
|
}
|
|
|
|
/* vScale. Tray must be a few pixels taller than cells to use same
|
|
sized, minimum-sized font. Subtract out that difference here and it's
|
|
guaranteed that tray will wind up at least that much taller later
|
|
though for the rest of the calculation we reserve only a cell's height
|
|
for it. */
|
|
vScale = (scrnHeight - (MIN_TRAY_HEIGHT-MIN_CELL_HEIGHT))
|
|
/ (nRows + (horiz?2:1)); /* horiz means scoreboard *and* tray */
|
|
if ( vScale >= MIN_CELL_HEIGHT ) {
|
|
nVisibleRows = nRows; /* no scrolling needed */
|
|
} else {
|
|
XP_U16 nRowsPossible = (scrnHeight-MIN_TRAY_HEIGHT) / MIN_CELL_HEIGHT;
|
|
XP_S16 num2Scroll;
|
|
num2Scroll = (nRows + (horiz?1:0)) - nRowsPossible; /* 1: scoreboard */
|
|
XP_ASSERT( num2Scroll > 0 );
|
|
if ( num2Scroll < 0 ) { /* no-scroll case should be caught above */
|
|
num2Scroll = 0;
|
|
}
|
|
nVisibleRows = nRows - num2Scroll;
|
|
vScale = (scrnHeight-MIN_TRAY_HEIGHT) / (nVisibleRows + (horiz? 1:0));
|
|
}
|
|
|
|
tmp = nRows + (horiz ? 0 : 2);
|
|
hScale = scrnWidth / tmp;
|
|
|
|
if ( vScale > hScale ) {
|
|
vScale = XP_MAX( MIN_CELL_HEIGHT, hScale );
|
|
} else if ( hScale > vScale ) {
|
|
hScale = XP_MAX( MIN_CELL_WIDTH, vScale );
|
|
}
|
|
|
|
/* Figure out tray size */
|
|
tmp = vScale * (nVisibleRows + (horiz? 1:0));
|
|
trayHeight = XP_MIN( vScale * 2, scrnHeight - tmp );
|
|
|
|
#ifdef CEFEATURE_CANSCROLL
|
|
/* Does this need to be in a loop? */
|
|
while ( nVisibleRows < nRows && hScale > MIN_CELL_WIDTH ) { /* need scroller? (while allows break) */
|
|
scrollWidth = scrnWidth - (tmp * hScale);
|
|
if ( scrollWidth >= MIN_SCROLLBAR_WIDTH ) {
|
|
break;
|
|
}
|
|
--hScale;
|
|
}
|
|
if ( scrollWidth > MAX_SCROLLBAR_WIDTH ) {
|
|
scrollWidth = MAX_SCROLLBAR_WIDTH;
|
|
}
|
|
#endif
|
|
|
|
if ( horiz ) {
|
|
scoreWidth = scrollWidth + (hScale * nRows);
|
|
scoreHeight = vScale - SCORE_TWEAK;
|
|
trayHeight += SCORE_TWEAK;
|
|
} else {
|
|
scoreWidth = XP_MIN( 2*hScale, scrnWidth - (hScale * nRows) );
|
|
scoreHeight = (nVisibleRows * vScale) + trayHeight;
|
|
}
|
|
/* XP_LOGF( "hScale=%d; vScale=%d; trayHeight=%d", hScale, vScale, trayHeight ); */
|
|
|
|
if ( globals->gameInfo.timerEnabled ) {
|
|
if ( horiz ) {
|
|
bparms->timerWidth = scoreWidth / 6; /* arbitrarily, one sixth */
|
|
scoreWidth -= bparms->timerWidth;
|
|
bparms->timerLeft = scoreWidth;
|
|
bparms->timerTop = 0;
|
|
bparms->timerHeight = scoreHeight;
|
|
} else {
|
|
bparms->timerLeft = 0;
|
|
bparms->timerHeight = vScale * 2;
|
|
bparms->timerTop = scoreHeight - bparms->timerHeight;
|
|
bparms->timerWidth = scoreWidth;
|
|
|
|
scoreHeight -= bparms->timerHeight;
|
|
}
|
|
}
|
|
|
|
globals->cellHt = vScale;
|
|
|
|
/* figure actual width and height */
|
|
tmp = scrollWidth + (hScale * nRows) + (horiz ? 0 : scoreWidth);
|
|
adjLeft = (scrnWidth - tmp)/2;
|
|
tmp = trayHeight + (vScale * nVisibleRows) + (horiz?scoreHeight:0);
|
|
adjTop = (scrnHeight - tmp)/2;
|
|
|
|
bparms->scrnWidth = scrnWidth;
|
|
bparms->scrnHeight = scrnHeight;
|
|
bparms->adjLeft = adjLeft;
|
|
bparms->adjTop = adjTop;
|
|
bparms->boardHScale = hScale;
|
|
bparms->boardVScale = vScale;
|
|
bparms->boardTop = adjTop + (horiz? scoreHeight : 0);
|
|
bparms->trayTop = bparms->boardTop + (nVisibleRows * vScale) + 1;
|
|
bparms->trayHeight = trayHeight - 1;
|
|
bparms->trayWidth = (hScale * nRows) + scrollWidth;
|
|
bparms->boardLeft = adjLeft + (horiz ? 0 : scoreWidth);
|
|
bparms->trayLeft = bparms->boardLeft;//horiz? 0 : scoreWidth;
|
|
bparms->scoreWidth = scoreWidth;
|
|
bparms->scoreHeight = scoreHeight;
|
|
bparms->scrollWidth = scrollWidth;
|
|
bparms->horiz = horiz;
|
|
|
|
#ifdef CEFEATURE_CANSCROLL
|
|
bparms->needsScroller = scrollWidth > 0;
|
|
if ( bparms->needsScroller ) {
|
|
XP_U16 boardRight;
|
|
boardRight = bparms->boardLeft + (nRows * hScale);
|
|
makeScrollbar( globals, nRows - nVisibleRows,
|
|
boardRight, bparms->boardTop,
|
|
scrollWidth,
|
|
vScale * nVisibleRows );
|
|
} else {
|
|
removeScrollbar( globals );
|
|
}
|
|
#endif
|
|
} /* figureBoardParms */
|
|
|
|
static void
|
|
setOwnedRects( CEAppGlobals* globals, XP_U16 nRows,
|
|
const CEBoardParms* bparms )
|
|
{
|
|
RECT tmp;
|
|
XP_U16 scrollWidth = bparms->scrollWidth;
|
|
|
|
XP_MEMSET( &globals->ownedRects, 0, sizeof(globals->ownedRects) );
|
|
|
|
tmp.top = bparms->adjTop; /* Same for both */
|
|
tmp.bottom = bparms->trayTop + bparms->trayHeight; /* Same for both */
|
|
|
|
tmp.left = 0;
|
|
tmp.right = bparms->adjLeft;
|
|
XP_MEMCPY( &globals->ownedRects[OWNED_RECT_LEFT], &tmp,
|
|
sizeof(globals->ownedRects[OWNED_RECT_LEFT]) );
|
|
|
|
tmp.left = tmp.right + (bparms->boardHScale * nRows) + scrollWidth;
|
|
tmp.right = bparms->scrnWidth;
|
|
XP_MEMCPY( &globals->ownedRects[OWNED_RECT_RIGHT], &tmp,
|
|
sizeof(globals->ownedRects[OWNED_RECT_RIGHT]) );
|
|
|
|
tmp.left = 0;
|
|
tmp.top = 0;
|
|
tmp.right = bparms->scrnWidth;
|
|
tmp.bottom = bparms->adjTop;
|
|
XP_MEMCPY( &globals->ownedRects[OWNED_RECT_TOP], &tmp,
|
|
sizeof(globals->ownedRects[OWNED_RECT_TOP]) );
|
|
|
|
tmp.top = bparms->trayTop + bparms->trayHeight;
|
|
tmp.bottom = bparms->scrnHeight;
|
|
XP_MEMCPY( &globals->ownedRects[OWNED_RECT_BOTTOM], &tmp,
|
|
sizeof(globals->ownedRects[OWNED_RECT_BOTTOM]) );
|
|
} /* setOwnedRects */
|
|
|
|
|
|
/* PENDING cePositionBoard gets called a lot when the screen size hasn't
|
|
changed. It'd be better to cache the size used to do layout and not
|
|
repeat those steps (including possibly nuking and rebuilding a
|
|
scrollbar). */
|
|
static XP_Bool
|
|
cePositionBoard( CEAppGlobals* globals )
|
|
{
|
|
XP_Bool erase = XP_FALSE;
|
|
XP_U16 nCols;
|
|
CEBoardParms bparms;
|
|
|
|
XP_ASSERT( !!globals->game.model );
|
|
nCols = model_numCols( globals->game.model );
|
|
XP_ASSERT( nCols <= CE_MAX_ROWS );
|
|
|
|
figureBoardParms( globals, nCols, &bparms );
|
|
setOwnedRects( globals, nCols, &bparms );
|
|
|
|
if ( globals->gameInfo.timerEnabled ) {
|
|
board_setTimerLoc( globals->game.board,
|
|
bparms.adjLeft + bparms.timerLeft,
|
|
bparms.adjTop + bparms.timerTop, bparms.timerWidth,
|
|
bparms.timerHeight );
|
|
}
|
|
|
|
board_setPos( globals->game.board, bparms.boardLeft,
|
|
bparms.boardTop, XP_FALSE );
|
|
board_setScale( globals->game.board, bparms.boardHScale,
|
|
bparms.boardVScale );
|
|
|
|
board_setScoreboardLoc( globals->game.board, bparms.adjLeft, bparms.adjTop,
|
|
bparms.scoreWidth,
|
|
bparms.scoreHeight, bparms.horiz );
|
|
board_setShowColors( globals->game.board, globals->appPrefs.showColors );
|
|
board_setYOffset( globals->game.board, 0 );
|
|
|
|
board_prefsChanged( globals->game.board, &globals->appPrefs.cp );
|
|
|
|
board_setTrayLoc( globals->game.board, bparms.trayLeft, bparms.trayTop,
|
|
bparms.trayWidth, bparms.trayHeight,
|
|
bparms.trayWidth/40 ); /* 1/8 of a tile width, roughly */
|
|
|
|
server_prefsChanged( globals->game.server, &globals->appPrefs.cp );
|
|
|
|
#if ! defined _WIN32_WCE && defined DEBUG
|
|
ceSetTitleFromName( globals );
|
|
#endif
|
|
ceCheckMenus( globals );
|
|
return erase;
|
|
} /* cePositionBoard */
|
|
|
|
/* Set the title. If there's a game name, replace the window title with that
|
|
* in case both won't fit. If there's no name yet, install the app name as
|
|
* title.
|
|
*/
|
|
static void
|
|
ceSetTitleFromName( CEAppGlobals* globals )
|
|
{
|
|
wchar_t widebuf[64];
|
|
const XP_UCHAR* gameName = globals->curGameName;
|
|
|
|
/* if default name, remove any current name */
|
|
if ( !gameName || isDefaultName( globals, gameName ) ) {
|
|
LoadString( globals->locInst, IDS_APP_TITLE, widebuf, VSIZE(widebuf) );
|
|
} else {
|
|
wchar_t* dotPos;
|
|
XP_UCHAR* baseStart = 1 + strrchr( gameName, '\\' );
|
|
XP_U16 len = (XP_U16)XP_STRLEN( baseStart );
|
|
|
|
MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, baseStart, len + 1,
|
|
widebuf, len + 1 );
|
|
|
|
/* now get rid of the ".xwg" */
|
|
dotPos = wcsrchr( widebuf, '.' );
|
|
if ( dotPos != NULL ) {
|
|
*dotPos = 0;
|
|
}
|
|
}
|
|
|
|
#if ! defined _WIN32_WCE && defined DEBUG
|
|
swprintf( &widebuf[wcslen(widebuf)], L":%dx%d",
|
|
globals->dbWidth, globals->dbHeight );
|
|
#endif
|
|
SendMessage( globals->hWnd, WM_SETTEXT, 0, (long)widebuf );
|
|
} /* ceSetTitleFromName */
|
|
|
|
static void
|
|
ceInitAndStartBoard( CEAppGlobals* globals, XP_Bool newGame,
|
|
const CommsAddrRec* XP_UNUSED_STANDALONE(addr) )
|
|
{
|
|
DictionaryCtxt* dict;
|
|
DictionaryCtxt* toBeDestroyed = NULL;
|
|
XP_UCHAR* newDictName = globals->gameInfo.dictName;
|
|
|
|
/* This needs to happen even when !newGame because it's how the dict
|
|
slots in PlayerInfo get loaded. That really ought to happen earlier,
|
|
though. */
|
|
XP_ASSERT( !!globals->game.model );
|
|
dict = model_getDictionary( globals->game.model );
|
|
|
|
if ( !!dict ) {
|
|
const XP_UCHAR* curDictName = dict_getName( dict );
|
|
if ( !!newDictName &&
|
|
( !curDictName || 0 != strcmp( curDictName, newDictName ) ) ) {
|
|
toBeDestroyed = dict;
|
|
dict = NULL;
|
|
} else {
|
|
replaceStringIfDifferent( globals->mpool,
|
|
&globals->gameInfo.dictName,
|
|
curDictName );
|
|
}
|
|
}
|
|
|
|
if ( !dict ) {
|
|
#ifdef STUBBED_DICT
|
|
dict = make_stubbed_dict( MPPARM_NOCOMMA(globals->mpool) );
|
|
#else
|
|
XP_ASSERT( !!newDictName );
|
|
XP_LOGF( "calling ce_dictionary_make" );
|
|
dict = ce_dictionary_make( globals, newDictName);
|
|
#endif
|
|
XP_ASSERT( !!dict );
|
|
model_setDictionary( globals->game.model, dict );
|
|
}
|
|
|
|
if ( newGame ) {
|
|
game_reset( MEMPOOL &globals->game, &globals->gameInfo, &globals->util,
|
|
0, &globals->appPrefs.cp, CE_SEND_PROC,
|
|
CE_RESET_PROC globals );
|
|
|
|
#if defined XWFEATURE_RELAY || defined XWFEATURE_BLUETOOTH
|
|
if ( !!addr ) {
|
|
XP_ASSERT( globals->game.comms != NULL );
|
|
comms_setAddr( globals->game.comms, addr );
|
|
}
|
|
#endif
|
|
}
|
|
|
|
XP_ASSERT( !!globals->game.board );
|
|
ceSizeIfFullscreen( globals, globals->hWnd );
|
|
(void)cePositionBoard( globals );
|
|
|
|
board_invalAll( globals->game.board );
|
|
InvalidateRect( globals->hWnd, NULL, TRUE /* erase */ );
|
|
|
|
#ifndef XWFEATURE_STANDALONE_ONLY
|
|
if ( !!globals->game.comms ) {
|
|
comms_start( globals->game.comms );
|
|
}
|
|
if ( newGame && globals->gameInfo.serverRole == SERVER_ISCLIENT ) {
|
|
XWStreamCtxt* stream;
|
|
XP_ASSERT( !!globals->game.comms );
|
|
stream = make_generic_stream( globals );
|
|
stream_setOnCloseProc( stream, ce_send_on_close );
|
|
server_initClientConnection( globals->game.server, stream );
|
|
}
|
|
#endif
|
|
|
|
ceSetLeftSoftkey( globals, ID_MOVE_TURNDONE );
|
|
|
|
server_do( globals->game.server );
|
|
|
|
globals->isNewGame = FALSE;
|
|
|
|
if ( !!toBeDestroyed ) {
|
|
dict_destroy( toBeDestroyed );
|
|
}
|
|
} /* ceInitAndStartBoard */
|
|
|
|
static XP_UCHAR*
|
|
ceReadString( const CEAppGlobals* globals, HANDLE fileH )
|
|
{
|
|
XP_U16 nameLen;
|
|
XP_UCHAR* name = NULL;
|
|
XP_U32 nRead;
|
|
|
|
if ( ReadFile( fileH, &nameLen, sizeof(nameLen), &nRead, NULL )
|
|
&& nameLen > 0 ) {
|
|
name = XP_MALLOC( globals->mpool, nameLen + 1 );
|
|
ReadFile( fileH, name, nameLen, &nRead, NULL );
|
|
name[nameLen] = '\0';
|
|
}
|
|
|
|
return name;
|
|
} /* ceReadString */
|
|
|
|
static void
|
|
ceWriteString( const XP_UCHAR* str, HANDLE fileH )
|
|
{
|
|
XP_U32 nWritten;
|
|
XP_U16 len = !!str? XP_STRLEN( str ) : 0;
|
|
WriteFile( fileH, &len, sizeof(len), &nWritten, NULL );
|
|
if ( 0 < len ) {
|
|
WriteFile( fileH, str, len, &nWritten, NULL );
|
|
}
|
|
}
|
|
|
|
static void
|
|
ceSavePrefs( CEAppGlobals* globals )
|
|
{
|
|
HANDLE fileH;
|
|
wchar_t path[CE_MAX_PATH_LEN];
|
|
|
|
(void)ceGetPath( globals, PREFS_FILE_PATH_L, path, VSIZE(path) );
|
|
fileH = CreateFile( path, GENERIC_WRITE, 0, NULL,
|
|
OPEN_ALWAYS, FILE_ATTRIBUTE_HIDDEN, NULL );
|
|
if ( fileH != INVALID_HANDLE_VALUE ) {
|
|
XP_U32 nWritten;
|
|
|
|
SetFilePointer( fileH, 0, 0, FILE_BEGIN );
|
|
/* write prefs, including version num */
|
|
WriteFile( fileH, &globals->appPrefs, sizeof(globals->appPrefs),
|
|
&nWritten, NULL );
|
|
|
|
ceWriteString( globals->curGameName, fileH );
|
|
|
|
WriteFile( fileH, &globals->flags, sizeof(globals->flags), &nWritten,
|
|
NULL );
|
|
|
|
ceWriteString( globals->langFileName, fileH );
|
|
|
|
SetEndOfFile( fileH ); /* truncate anything previously there */
|
|
|
|
CloseHandle( fileH ); /* am I not supposed to do this? PENDING */
|
|
XP_DEBUGF( "ceSavePrefs: prefs file written" );
|
|
} else {
|
|
logLastError( "failed to create prefs file" );
|
|
}
|
|
} /* ceSavePrefs */
|
|
|
|
static XP_Bool
|
|
peekVersion( HANDLE fileH, XP_U16* version )
|
|
{
|
|
XP_Bool success = XP_FALSE;
|
|
XP_U32 nRead;
|
|
success = ReadFile( fileH, version, sizeof(*version), &nRead, NULL );
|
|
if ( success ) {
|
|
SetFilePointer( fileH, -nRead, 0, FILE_CURRENT );
|
|
}
|
|
return success;
|
|
} /* peekVersion */
|
|
|
|
static XP_Bool
|
|
canUpdatePrefs( CEAppGlobals* globals, HANDLE fileH, XP_U16 curVersion,
|
|
CEAppPrefs* prefs )
|
|
{
|
|
XP_Bool success = XP_FALSE;
|
|
LOG_FUNC();
|
|
if ( (curVersion == 0x0002) && (CUR_CE_PREFS_FLAGS == 0x0003) ) {
|
|
CEAppPrefs0002 oldPrefs;
|
|
XP_U32 nRead;
|
|
if ( ReadFile( fileH, &oldPrefs, sizeof(oldPrefs), &nRead, NULL ) ) {
|
|
ceInitPrefs( globals, prefs );
|
|
|
|
XP_MEMCPY( &prefs->cp, &oldPrefs.cp, sizeof(prefs->cp) );
|
|
prefs->showColors = oldPrefs.showColors;
|
|
|
|
XP_MEMCPY( &prefs->colors[0], &oldPrefs.colors[0],
|
|
CE_FOCUS_COLOR*sizeof(prefs->colors[0]));
|
|
XP_ASSERT( CE_PLAYER0_COLOR - 1 == CE_FOCUS_COLOR );
|
|
XP_MEMCPY( &prefs->colors[CE_PLAYER0_COLOR],
|
|
&oldPrefs.colors[CE_FOCUS_COLOR],
|
|
(CE_NUM_COLORS-CE_PLAYER0_COLOR)
|
|
* sizeof(prefs->colors[0]));
|
|
success = XP_TRUE;
|
|
} else {
|
|
XP_LOGF( "%s: ReadFile bad", __func__ );
|
|
}
|
|
} else {
|
|
XP_LOGF( "%s: can't convert from %d to %d", __func__,
|
|
curVersion, CUR_CE_PREFS_FLAGS );
|
|
}
|
|
LOG_RETURNF( "%d", (int)success );
|
|
return success;
|
|
} /* canUpdatePrefs */
|
|
|
|
static XP_Bool
|
|
ceLoadPrefs( CEAppGlobals* globals )
|
|
{
|
|
XP_Bool result = XP_FALSE;
|
|
HANDLE fileH;
|
|
wchar_t path[CE_MAX_PATH_LEN];
|
|
|
|
(void)ceGetPath( globals, PREFS_FILE_PATH_L, path, VSIZE(path) );
|
|
fileH = CreateFile( path, GENERIC_READ, 0, NULL,
|
|
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
|
|
if ( fileH != INVALID_HANDLE_VALUE ) {
|
|
XP_U32 fileSize = GetFileSize( fileH, NULL );
|
|
XP_U16 curVersion;
|
|
if ( fileSize >= sizeof(curVersion) && peekVersion( fileH,
|
|
&curVersion ) ) {
|
|
CEAppPrefs tmpPrefs;
|
|
if ( curVersion == CUR_CE_PREFS_FLAGS ) {
|
|
if ( fileSize >= sizeof( CEAppPrefs ) ) {
|
|
XP_U32 bytesRead;
|
|
if ( ReadFile( fileH, &tmpPrefs, sizeof(tmpPrefs),
|
|
&bytesRead, NULL ) ) {
|
|
XP_ASSERT( tmpPrefs.versionFlags == CUR_CE_PREFS_FLAGS );
|
|
result = XP_TRUE;
|
|
}
|
|
}
|
|
} else if ( canUpdatePrefs( globals, fileH, curVersion,
|
|
&tmpPrefs ) ) {
|
|
result = XP_TRUE;
|
|
} else {
|
|
XP_LOGF( "%s: old prefs; cannot read.", __func__ );
|
|
}
|
|
|
|
if ( result ) {
|
|
XP_U16 flags;
|
|
XP_U32 nRead;
|
|
|
|
XP_MEMCPY( &globals->appPrefs, &tmpPrefs,
|
|
sizeof(globals->appPrefs) );
|
|
|
|
globals->curGameName = ceReadString( globals, fileH );
|
|
|
|
if ( ReadFile( fileH, &flags, sizeof(flags), &nRead,
|
|
NULL )
|
|
&& nRead == sizeof(flags) ) {
|
|
} else {
|
|
flags = 0;
|
|
}
|
|
globals->flags = flags;
|
|
|
|
globals->langFileName = ceReadString( globals, fileH );
|
|
}
|
|
}
|
|
CloseHandle( fileH );
|
|
} else {
|
|
XP_LOGF( "ceLoadPrefs: prefs file not found" );
|
|
}
|
|
return result; /* none found */
|
|
} /* ceLoadPrefs */
|
|
|
|
static XWStreamCtxt*
|
|
make_generic_stream( const CEAppGlobals* globals )
|
|
{
|
|
return mem_stream_make( MPPARM(globals->mpool) globals->vtMgr,
|
|
(void*)globals, 0, NULL );
|
|
} /* make_generic_stream */
|
|
|
|
static XWStreamCtxt*
|
|
fileToStream( const CEAppGlobals* globals, const XP_UCHAR* path )
|
|
{
|
|
XWStreamCtxt* stream = NULL;
|
|
HANDLE fileH;
|
|
wchar_t widebuf[257];
|
|
XP_U16 len;
|
|
|
|
len = (XP_U16)XP_STRLEN( path );
|
|
MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, path, len + 1,
|
|
widebuf, len + 1 );
|
|
|
|
fileH = CreateFile( widebuf, GENERIC_READ, 0, NULL,
|
|
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
|
|
|
|
if ( fileH != INVALID_HANDLE_VALUE ) {
|
|
XP_U32 len = GetFileSize( fileH, NULL );
|
|
XP_U32 nRead;
|
|
XP_UCHAR* buf = XP_MALLOC( globals->mpool, len );
|
|
|
|
XP_DEBUGF( "fileSize = %ld", len );
|
|
|
|
ReadFile( fileH, buf, len, &nRead, NULL );
|
|
CloseHandle( fileH );
|
|
|
|
stream = make_generic_stream( globals );
|
|
stream_open( stream );
|
|
stream_putBytes( stream, buf, (XP_U16)nRead );
|
|
|
|
XP_FREE( globals->mpool, buf );
|
|
}
|
|
|
|
XP_DEBUGF( "fileToStream => 0x%lx", (unsigned long)stream );
|
|
|
|
return stream;
|
|
} /* fileToStream */
|
|
|
|
static XP_Bool
|
|
ceLoadSavedGame( CEAppGlobals* globals )
|
|
{
|
|
XP_Bool success = XP_FALSE;
|
|
XWStreamCtxt* stream;
|
|
|
|
LOG_FUNC();
|
|
|
|
stream = fileToStream( globals, globals->curGameName );
|
|
success = stream != NULL;
|
|
if ( success ) {
|
|
DictionaryCtxt* dict;
|
|
XP_U8 flags = stream_getU8( stream );
|
|
XP_Bool hasDict = (flags & 0x01) != 0;
|
|
flags >>= 1;
|
|
|
|
if ( hasDict ) {
|
|
#ifdef STUBBED_DICT
|
|
XP_ASSERT(0); /* just don't do this!!!! */
|
|
#else
|
|
XP_UCHAR* dictName = stringFromStream( globals->mpool, stream );
|
|
dict = ce_dictionary_make( globals, dictName );
|
|
success = dict != NULL;
|
|
if ( !success ) {
|
|
XP_UCHAR buf[128];
|
|
snprintf( buf, VSIZE(buf),
|
|
ceGetResString( globals, IDS_CANNOTOPEN_DICT ),
|
|
dictName );
|
|
buf[VSIZE(buf)-1] = '\0';
|
|
ceOops( globals, buf );
|
|
|
|
}
|
|
XP_FREE( globals->mpool, dictName );
|
|
#endif
|
|
} else {
|
|
dict = NULL;
|
|
}
|
|
|
|
if ( success ) {
|
|
if ( flags >= CE_GAMEFILE_VERSION1 ) {
|
|
ce_draw_fromStream( globals->draw, stream, flags );
|
|
}
|
|
|
|
XP_DEBUGF( "calling game_makeFromStream" );
|
|
success = game_makeFromStream( MEMPOOL stream, &globals->game,
|
|
&globals->gameInfo, dict,
|
|
&globals->util,
|
|
(DrawCtx*)globals->draw,
|
|
&globals->appPrefs.cp, CE_SEND_PROC,
|
|
CE_RESET_PROC globals );
|
|
if ( success ) {
|
|
ceSetTitleFromName( globals );
|
|
} else {
|
|
if ( !!dict ) {
|
|
dict_destroy( dict );
|
|
}
|
|
ceOops( globals,
|
|
ceGetResString( globals, IDS_CANNOTOPEN_GAME ) );
|
|
}
|
|
}
|
|
|
|
stream_destroy( stream );
|
|
}
|
|
|
|
return success;
|
|
} /* ceLoadSavedGame */
|
|
|
|
static void
|
|
colorsFromRsrc( const CEAppGlobals* globals, CEAppPrefs* prefs )
|
|
{
|
|
XP_U16 i;
|
|
HGLOBAL globH;
|
|
HRSRC rsrcH;
|
|
XP_U16* ptr;
|
|
|
|
rsrcH = FindResource( globals->hInst, MAKEINTRESOURCE(ID_COLORS_RES),
|
|
TEXT("CLRS") );
|
|
globH = LoadResource( globals->hInst, rsrcH );
|
|
ptr = (XP_U16*)globH;
|
|
|
|
for ( i = 0; i < CE_NUM_COLORS; ++i ) {
|
|
XP_U8 r = (XP_U8)*ptr++;
|
|
XP_U8 g = (XP_U8)*ptr++;
|
|
XP_U8 b = (XP_U8)*ptr++;
|
|
prefs->colors[i] = RGB( r, g, b );
|
|
}
|
|
|
|
DeleteObject( globH );
|
|
} /* colorsFromRsrc */
|
|
|
|
static void
|
|
ceInitPrefs( CEAppGlobals* globals, CEAppPrefs* prefs )
|
|
{
|
|
prefs->versionFlags = CUR_CE_PREFS_FLAGS;
|
|
prefs->showColors = XP_TRUE;
|
|
prefs->fullScreen = XP_FALSE;
|
|
|
|
prefs->cp.showBoardArrow = XP_TRUE;
|
|
prefs->cp.showRobotScores = XP_FALSE;
|
|
|
|
colorsFromRsrc( globals, prefs );
|
|
} /* ceInitPrefs */
|
|
|
|
#ifdef _WIN32_WCE
|
|
static void
|
|
getOSInfo( CEAppGlobals* globals )
|
|
{
|
|
OSVERSIONINFO ver = {0};
|
|
TCHAR buf[128];
|
|
XW_WinceVersion winceVersion = WINCE_UNKNOWN;
|
|
|
|
if ( GetVersionEx( &ver )) {
|
|
XP_LOGF( "version = %ld.%ld", ver.dwMajorVersion, ver.dwMinorVersion );
|
|
} else {
|
|
XP_WARNF( "GetVersionEx failed" );
|
|
}
|
|
|
|
if ( SystemParametersInfo( SPI_GETPLATFORMTYPE, sizeof(buf), buf, FALSE ) ) {
|
|
if ( 0 == lstrcmp( buf, L"PocketPC") ) {
|
|
// We are on a Pocket PC, so check the OS version,
|
|
// Pocket PC 2003 used WinCE 4.2
|
|
if ( ver.dwMajorVersion < 4 ) {
|
|
winceVersion = WINCE_PPC_V1;
|
|
} else if ( ver.dwMajorVersion == 4 ) {
|
|
winceVersion = WINCE_PPC_2003;
|
|
} else if ( ver.dwMajorVersion > 4 ) {
|
|
winceVersion = WINCE_PPC_2005;
|
|
}
|
|
} else if ( 0 == lstrcmp( buf, L"SmartPhone") ) {
|
|
if ( ver.dwMajorVersion < 4 ) {
|
|
winceVersion = WINCE_SMARTPHONE_V1;
|
|
} else if ( ver.dwMajorVersion == 4 ) {
|
|
winceVersion = WINCE_SMARTPHONE_2003;
|
|
} else if ( ver.dwMajorVersion > 4 ) {
|
|
winceVersion = WINCE_SMARTPHONE_2005;
|
|
}
|
|
} else {
|
|
XP_LOGW( "unknown OS type", buf );
|
|
}
|
|
} else if ( GetLastError() == ERROR_ACCESS_DENIED ) {
|
|
if ( ver.dwMajorVersion < 4 ) {
|
|
winceVersion = WINCE_SMARTPHONE_V1;
|
|
} else {
|
|
winceVersion = WINCE_SMARTPHONE_2003;
|
|
}
|
|
}
|
|
|
|
XP_ASSERT( winceVersion != WINCE_UNKNOWN );
|
|
globals->winceVersion = winceVersion;
|
|
XP_LOGF( "%s: set version to %d", __func__, winceVersion );
|
|
} /* getOSInfo */
|
|
#else
|
|
#define getOSInfo( g )
|
|
#endif
|
|
|
|
//
|
|
// FUNCTION: InitInstance(HANDLE, int)
|
|
//
|
|
// PURPOSE: Saves instance handle and creates main window
|
|
//
|
|
// COMMENTS:
|
|
//
|
|
// In this function, we save the instance handle in a global variable and
|
|
// create and display the main program window.
|
|
//
|
|
BOOL
|
|
InitInstance(HINSTANCE hInstance, int nCmdShow
|
|
#ifndef _WIN32_WCE
|
|
,XP_U16 width, XP_U16 height, const char* dll
|
|
#endif
|
|
)
|
|
{
|
|
HWND hWnd;
|
|
TCHAR szTitle[MAX_LOADSTRING]; // The title bar text
|
|
TCHAR szWindowClass[MAX_LOADSTRING]; // The window class name
|
|
CEAppGlobals* globals;
|
|
BOOL result = FALSE;
|
|
XP_Bool oldGameLoaded;
|
|
XP_Bool prevStateExists;
|
|
XP_Bool newDone = XP_FALSE;
|
|
wchar_t path[CE_MAX_PATH_LEN];
|
|
MPSLOT;
|
|
|
|
#ifndef XWFEATURE_STANDALONE_ONLY
|
|
{
|
|
WORD wVersionRequested;
|
|
WSADATA wsaData;
|
|
int err;
|
|
wVersionRequested = MAKEWORD( 2, 2 );
|
|
err = WSAStartup( wVersionRequested, &wsaData );
|
|
if ( err != 0 ) {
|
|
/* Tell the user that we could not find a usable */
|
|
/* WinSock DLL. */
|
|
XP_LOGF( "unable to load winsock" );
|
|
}
|
|
}
|
|
#endif
|
|
|
|
LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
|
|
LoadString(hInstance, IDC_XWORDS4, szWindowClass, MAX_LOADSTRING);
|
|
|
|
// If it is already running, then focus on the window. Skip title in
|
|
// search as we change title to include game name
|
|
hWnd = FindWindow( szWindowClass, NULL );
|
|
if ( hWnd ) {
|
|
SetForegroundWindow( (HWND)((ULONG) hWnd | 0x00000001) );
|
|
goto exit;
|
|
}
|
|
|
|
#ifdef MEM_DEBUG
|
|
mpool = mpool_make();
|
|
#endif
|
|
|
|
globals = (CEAppGlobals*)XP_MALLOC( mpool, sizeof(*globals) );
|
|
XP_DEBUGF( "globals created: 0x%p", globals );
|
|
XP_MEMSET( globals, 0, sizeof(*globals) );
|
|
MPASSIGN( globals->mpool, mpool );
|
|
|
|
#ifndef _WIN32_WCE
|
|
globals->dbWidth = width;
|
|
globals->dbHeight = height;
|
|
#endif
|
|
|
|
(void)ceGetPath( globals, DEFAULT_DIR_PATH_L, path, VSIZE(path) );
|
|
(void)CreateDirectory( path, 0 );
|
|
|
|
getOSInfo( globals );
|
|
|
|
globals->vtMgr = make_vtablemgr( MPPARM_NOCOMMA(mpool) );
|
|
|
|
globals->hInst = hInstance;
|
|
// Initialize global strings
|
|
MyRegisterClass(hInstance, szWindowClass);
|
|
|
|
prevStateExists = ceLoadPrefs( globals );
|
|
if ( !prevStateExists ) {
|
|
ceInitPrefs( globals, &globals->appPrefs );
|
|
}
|
|
|
|
#ifndef _WIN32_WCE
|
|
srand( time(NULL) );
|
|
#endif
|
|
|
|
/* Was a language file named in preferences? If so, and if none was
|
|
provided on the cmdline, load it (if it exists; if it doesn't, act as
|
|
if none set). */
|
|
#ifndef _WIN32_WCE
|
|
if ( !!dll ) {
|
|
replaceStringIfDifferent( globals->mpool, &globals->langFileName, dll );
|
|
}
|
|
#else
|
|
/* This french hard-coding thing is temporary! */
|
|
replaceStringIfDifferent( globals->mpool, &globals->langFileName, "xwords4_french.dll" );
|
|
#endif
|
|
|
|
if ( !!globals->langFileName && !globals->locInst ) {
|
|
HINSTANCE inst = ceLoadResFile( globals->langFileName );
|
|
if ( !!inst ) {
|
|
globals->locInst = inst;
|
|
} else {
|
|
XP_FREE( globals->mpool, globals->langFileName );
|
|
globals->langFileName = NULL;
|
|
}
|
|
}
|
|
if ( !globals->locInst ) {
|
|
globals->locInst = globals->hInst;
|
|
}
|
|
|
|
hWnd = CreateWindow( szWindowClass, szTitle, WS_VISIBLE,
|
|
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
|
|
CW_USEDEFAULT, NULL, NULL, hInstance, globals );
|
|
if (!hWnd) {
|
|
goto exit;
|
|
}
|
|
globals->hWnd = hWnd;
|
|
|
|
#ifdef _WIN32_WCE
|
|
if ( globals->hwndCB && !IS_SMARTPHONE(globals) ) {
|
|
RECT rc, rcmb;
|
|
|
|
GetWindowRect( hWnd, &rc );
|
|
GetWindowRect( globals->hwndCB, &rcmb );
|
|
rc.bottom -= (rcmb.bottom - rcmb.top);
|
|
|
|
MoveWindow( hWnd, rc.left, rc.top, rc.right-rc.left,
|
|
rc.bottom-rc.top, FALSE );
|
|
}
|
|
#endif
|
|
|
|
ceInitUtilFuncs( globals );
|
|
|
|
gi_initPlayerInfo( MPPARM(mpool) &globals->gameInfo, NULL );
|
|
|
|
|
|
#ifndef _WIN32_WCE
|
|
SetMenu( hWnd, LoadMenu( globals->locInst, MAKEINTRESOURCE(IDM_MENU) ) );
|
|
#endif
|
|
|
|
/* choose one. If none found it's an error. */
|
|
#ifndef STUBBED_DICT
|
|
result = 1 == ceLocateNDicts( globals, 1, ceSetDictName,
|
|
globals );
|
|
if ( !result ) {
|
|
wchar_t buf[512];
|
|
(void)LoadString( globals->locInst, (UINT)IDS_DICTLOC, buf, VSIZE(buf) );
|
|
assertOnTop( globals->hWnd );
|
|
MessageBox( globals->hWnd, buf,
|
|
ceGetResStringL( globals, IDS_NODICT_L ),
|
|
MB_OK | MB_ICONHAND );
|
|
goto exit;
|
|
}
|
|
#else
|
|
result = TRUE;
|
|
#endif
|
|
|
|
|
|
/* must load prefs before creating draw ctxt */
|
|
globals->draw = ce_drawctxt_make( MPPARM(globals->mpool)
|
|
hWnd, globals );
|
|
|
|
oldGameLoaded = prevStateExists && ceLoadSavedGame( globals );
|
|
|
|
if ( !oldGameLoaded ) {
|
|
game_makeNewGame( MPPARM(mpool) &globals->game, &globals->gameInfo,
|
|
&globals->util, (DrawCtx*)globals->draw, 0,
|
|
&globals->appPrefs.cp,
|
|
CE_SEND_PROC, CE_RESET_PROC globals );
|
|
|
|
newDone = ceDoNewGame( globals ); /* calls ceInitAndStartBoard */
|
|
if ( !newDone ) {
|
|
result = FALSE;
|
|
}
|
|
}
|
|
|
|
trapBackspaceKey( hWnd );
|
|
|
|
ShowWindow(hWnd, nCmdShow);
|
|
UpdateWindow(hWnd);
|
|
#ifdef _WIN32_WCE
|
|
if (globals->hwndCB) {
|
|
CommandBar_Show(globals->hwndCB, TRUE);
|
|
}
|
|
#endif
|
|
if ( result && !newDone ) {
|
|
ceInitAndStartBoard( globals, !oldGameLoaded, NULL );
|
|
}
|
|
|
|
exit:
|
|
return result;
|
|
} /* InitInstance */
|
|
|
|
static XP_Bool
|
|
ceSetDictName( const wchar_t* XP_UNUSED(wPath), XP_U16 XP_UNUSED_DBG(index),
|
|
void* XP_UNUSED(ctxt) )
|
|
{
|
|
/* CEAppGlobals* globals = (CEAppGlobals*)ctxt; */
|
|
/* XP_UCHAR* str; */
|
|
/* XP_UCHAR buf[CE_MAX_PATH_LEN]; */
|
|
XP_ASSERT( index == 0 ); /* we said one only! */
|
|
|
|
/* WideCharToMultiByte( CP_ACP, 0, wPath, -1, */
|
|
/* buf, sizeof(buf)-1, NULL, NULL ); */
|
|
|
|
/* XP_LOGF( "%s: got path \"%s\"", __func__, buf ); */
|
|
/* str = copyString( MPPARM(globals->mpool) buf ); */
|
|
/* XP_LOGF( "%s: got %p", __func__, str ); /\* this is leaking *\/ */
|
|
/* XP_ASSERT( NULL == globals->gameInfo.dictName ); */
|
|
/* globals->gameInfo.dictName = str; */
|
|
return XP_FALSE;
|
|
} /* ceSetDictName */
|
|
|
|
static XP_Bool
|
|
ceHandleHintRequest( CEAppGlobals* globals )
|
|
{
|
|
XP_Bool notDone;
|
|
XP_Bool draw;
|
|
XP_ASSERT( !!globals->game.board );
|
|
|
|
draw = board_requestHint( globals->game.board,
|
|
#ifdef XWFEATURE_SEARCHLIMIT
|
|
globals->askTrayLimits,
|
|
#endif
|
|
¬Done );
|
|
globals->hintPending = notDone;
|
|
if ( draw ) { /* don't turn on if disallowed */
|
|
ceSetLeftSoftkey( globals, ID_MOVE_NEXTHINT );
|
|
}
|
|
return draw;
|
|
} /* ceHandleHintRequest */
|
|
|
|
static XP_Bool
|
|
handleTradeCmd( CEAppGlobals* globals )
|
|
{
|
|
XP_Bool success = board_beginTrade( globals->game.board );
|
|
if ( success ) {
|
|
ceSetLeftSoftkey( globals, ID_MOVE_TURNDONE );
|
|
}
|
|
return success;
|
|
} /* handleTradeCmd */
|
|
|
|
static XP_Bool
|
|
handleJuggleCmd( CEAppGlobals* globals )
|
|
{
|
|
ceSetLeftSoftkey( globals, ID_MOVE_JUGGLE );
|
|
return board_juggleTray( globals->game.board );
|
|
} /* handleJuggleCmd */
|
|
|
|
static XP_Bool
|
|
handleHidetrayCmd( CEAppGlobals* globals )
|
|
{
|
|
XP_Bool result;
|
|
XW_TrayVisState curState = board_getTrayVisState( globals->game.board );
|
|
|
|
if ( curState == TRAY_REVEALED ) {
|
|
result = board_hideTray( globals->game.board );
|
|
} else {
|
|
result = board_showTray( globals->game.board );
|
|
}
|
|
|
|
ceSetLeftSoftkey( globals, ID_MOVE_HIDETRAY );
|
|
return result;
|
|
} /* handleHidetrayCmd */
|
|
|
|
static XP_Bool
|
|
handleDoneCmd( CEAppGlobals* globals)
|
|
{
|
|
return board_commitTurn( globals->game.board );
|
|
} /* handleDoneCmd */
|
|
|
|
static void
|
|
ceCountsAndValues( CEAppGlobals* globals )
|
|
{
|
|
if ( !!globals->game.server ) {
|
|
XWStreamCtxt* stream = make_generic_stream( globals );
|
|
|
|
server_formatDictCounts( globals->game.server, stream, 3 );
|
|
|
|
(void)ceMsgFromStream( globals, stream,
|
|
ceGetResStringL( globals, IDS_COUNTSVALS_L ),
|
|
MB_OK | MB_ICONINFORMATION, XP_TRUE );
|
|
}
|
|
} /* ceCountsAndValues */
|
|
|
|
static void
|
|
ceTilesLeft( CEAppGlobals* globals )
|
|
{
|
|
if ( !!globals->game.board ) {
|
|
XWStreamCtxt* stream = make_generic_stream( globals );
|
|
board_formatRemainingTiles( globals->game.board, stream );
|
|
|
|
(void)ceMsgFromStream( globals, stream,
|
|
ceGetResStringL( globals, IDS_REMTILES_L ),
|
|
MB_OK | MB_ICONINFORMATION, XP_TRUE );
|
|
}
|
|
} /* ceTilesLeft */
|
|
|
|
static void
|
|
ceDoHistory( CEAppGlobals* globals )
|
|
{
|
|
XP_Bool gameOver = server_getGameIsOver(globals->game.server);
|
|
XWStreamCtxt* stream;
|
|
|
|
stream = make_generic_stream( globals );
|
|
|
|
model_writeGameHistory( globals->game.model, stream,
|
|
globals->game.server, gameOver );
|
|
(void)ceMsgFromStream( globals, stream,
|
|
ceGetResStringL( globals, IDS_GAMEHIST_L ),
|
|
MB_OK | MB_ICONINFORMATION, XP_TRUE );
|
|
} /* ceDoHistory */
|
|
|
|
static void
|
|
drawInsidePaint( CEAppGlobals* globals, const RECT* invalR )
|
|
{
|
|
HDC hdc;
|
|
|
|
hdc = GetDC( globals->hWnd );
|
|
if ( !hdc ) {
|
|
logLastError( __func__ );
|
|
} else {
|
|
int oldMode = SetBkMode( hdc, TRANSPARENT );
|
|
HDC prevHDC = globals->hdc;
|
|
globals->hdc = hdc;
|
|
|
|
if ( !!invalR ) {
|
|
XP_U16 ii;
|
|
RECT interR;
|
|
for ( ii = 0; ii < N_OWNED_RECTS; ++ii ) {
|
|
if ( IntersectRect( &interR, invalR,
|
|
&globals->ownedRects[ii] ) ) {
|
|
ce_draw_erase( globals->draw, &interR );
|
|
}
|
|
}
|
|
#ifdef _WIN32_WCE
|
|
for ( ii = 0; ii < VSIZE(globals->scrollRects); ++ii ) {
|
|
if ( IntersectRect( &interR, invalR,
|
|
&globals->scrollRects[ii] ) ) {
|
|
if ( globals->scrollerHasFocus ) {
|
|
ce_draw_focus( globals->draw, &interR );
|
|
} else {
|
|
ce_draw_erase( globals->draw, &interR );
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
board_draw( globals->game.board );
|
|
|
|
(void)SetBkMode( hdc, oldMode );
|
|
globals->hdc = prevHDC;
|
|
}
|
|
} /* drawInsidePaint */
|
|
|
|
static void
|
|
ceDisplayFinalScores( CEAppGlobals* globals )
|
|
{
|
|
XWStreamCtxt* stream;
|
|
|
|
stream = make_generic_stream( globals );
|
|
server_writeFinalScores( globals->game.server, stream );
|
|
stream_putU8( stream, '\0' );
|
|
|
|
(void)ceMsgFromStream( globals, stream,
|
|
ceGetResStringL( globals, IDS_FINALSCORE_L),
|
|
MB_OK | MB_ICONINFORMATION, XP_TRUE );
|
|
} /* ceDisplayFinalScores */
|
|
|
|
static XP_Bool
|
|
ceDoNewGame( CEAppGlobals* globals )
|
|
{
|
|
GameInfoState giState;
|
|
CommsAddrRec* addr = NULL;
|
|
XP_Bool changed = XP_FALSE;
|
|
|
|
XP_MEMSET( &giState, 0, sizeof(giState) );
|
|
giState.dlgHdr.globals = globals;
|
|
giState.isNewGame = XP_TRUE;
|
|
|
|
assertOnTop( globals->hWnd );
|
|
DialogBoxParam( globals->locInst, (LPCTSTR)IDD_GAMEINFO, globals->hWnd,
|
|
(DLGPROC)GameInfo, (long)&giState );
|
|
|
|
if ( !giState.userCancelled
|
|
#ifndef STUBBED_DICT
|
|
&& ( giState.newDictName[0] != '\0' )
|
|
#endif
|
|
) {
|
|
|
|
if ( globals->curGameName != NULL ) {
|
|
XP_FREE( globals->mpool, globals->curGameName );
|
|
globals->curGameName = NULL;
|
|
}
|
|
|
|
if ( giState.prefsChanged ) {
|
|
loadCurPrefsFromState( globals, &globals->appPrefs,
|
|
&globals->gameInfo, &giState.prefsPrefs );
|
|
if ( giState.colorsChanged ) {
|
|
updateForColors( globals );
|
|
}
|
|
}
|
|
#if defined XWFEATURE_RELAY || defined XWFEATURE_BLUETOOTH
|
|
if ( giState.addrChanged ) {
|
|
addr = &giState.prefsPrefs.addrRec;
|
|
}
|
|
#endif
|
|
|
|
ceInitAndStartBoard( globals, XP_TRUE, addr );
|
|
ceSetTitleFromName( globals );
|
|
changed = XP_TRUE;
|
|
}
|
|
|
|
return changed;
|
|
} /* ceDoNewGame */
|
|
|
|
static void
|
|
ceChooseAndOpen( CEAppGlobals* globals )
|
|
{
|
|
assertOnTop( globals->hWnd );
|
|
// Save in case we'll be duplicating it
|
|
again:
|
|
if ( ceSaveCurGame( globals, XP_FALSE ) ) {
|
|
SavedGamesResult choice;
|
|
wchar_t newName[256];
|
|
newName[0] = 0;
|
|
|
|
ceSetTitleFromName( globals ); /* in case we named it above */
|
|
|
|
choice = ceSavedGamesDlg( globals, globals->curGameName, newName,
|
|
VSIZE(newName) );
|
|
if ( CE_SVGAME_CANCEL != choice ) {
|
|
XP_UCHAR* name;
|
|
XP_U16 len;
|
|
|
|
len = wcslen(newName);
|
|
name = XP_MALLOC( globals->mpool, len + 1 );
|
|
|
|
WideCharToMultiByte( CP_ACP, 0, newName, len + 1,
|
|
name, len + 1, NULL, NULL );
|
|
|
|
if ( globals->curGameName != NULL
|
|
&& 0 == XP_STRCMP( name, globals->curGameName ) ){
|
|
/* User chose already-open game; no-op */
|
|
XP_FREE( globals->mpool, name );
|
|
} else {
|
|
/* Save old name in case fail to open new, e.g. because dict
|
|
not there */
|
|
XP_UCHAR* oldName;
|
|
|
|
/* Need to save a second time, with auto-save, in case user
|
|
wants to overwrite yet chooses a game whose dict is
|
|
missing -- since then we'll be re-opening this game! */
|
|
ceSaveCurGame( globals, XP_TRUE ); /* may change curGameName */
|
|
|
|
oldName = globals->curGameName;
|
|
globals->curGameName = NULL; /* prevent being destroyed */
|
|
closeGame( globals );
|
|
|
|
if ( CE_SVGAME_RENAME == choice ) {
|
|
XP_U16 len = 1 + XP_STRLEN( oldName );
|
|
wchar_t widebuf[len];
|
|
MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, oldName, len,
|
|
widebuf, len );
|
|
(void)MoveFile( widebuf, newName );
|
|
}
|
|
|
|
globals->curGameName = name;
|
|
if ( ceLoadSavedGame( globals ) ) {
|
|
XP_FREE( globals->mpool, oldName );
|
|
} else {
|
|
XP_ASSERT( CE_SVGAME_RENAME != choice );
|
|
XP_LOGF( "failed to open chosen game" );
|
|
XP_FREE( globals->mpool, globals->curGameName );
|
|
globals->curGameName = oldName;
|
|
if ( !ceLoadSavedGame( globals ) ) {
|
|
XP_LOGF( "failed to open old game too!!!" );
|
|
}
|
|
}
|
|
ceInitAndStartBoard( globals, XP_FALSE, NULL );
|
|
if ( CE_SVGAME_RENAME == choice ) {
|
|
goto again;
|
|
}
|
|
}
|
|
} else {
|
|
XP_LOGF( "GetOpenFileName() failed" );
|
|
}
|
|
}
|
|
} /* ceChooseAndOpen */
|
|
|
|
static void
|
|
updateForColors( CEAppGlobals* globals )
|
|
{
|
|
ce_draw_update( globals->draw );
|
|
if ( !!globals->game.board ) {
|
|
board_invalAll( globals->game.board );
|
|
}
|
|
} /* updateForColors */
|
|
|
|
static void
|
|
ceDoPrefsDlg( CEAppGlobals* globals )
|
|
{
|
|
CePrefsDlgState state;
|
|
CePrefsPrefs prefsPrefs;
|
|
|
|
loadStateFromCurPrefs( globals, &globals->appPrefs, &globals->gameInfo,
|
|
&prefsPrefs );
|
|
|
|
assertOnTop( globals->hWnd );
|
|
(void)WrapPrefsDialog( globals->hWnd, globals, &state, &prefsPrefs,
|
|
XP_FALSE );
|
|
|
|
if ( !state.userCancelled ) {
|
|
|
|
loadCurPrefsFromState( globals, &globals->appPrefs, &globals->gameInfo,
|
|
&prefsPrefs );
|
|
|
|
(void)cePositionBoard( globals );
|
|
|
|
if ( state.colorsChanged ) {
|
|
updateForColors( globals );
|
|
}
|
|
/* need to reflect vars set in state into globals, and update/inval
|
|
as appropriate. */
|
|
}
|
|
} /* ceDoPrefsDlg */
|
|
|
|
static void
|
|
ceWriteToFile( XWStreamCtxt* stream, void* closure )
|
|
{
|
|
FileWriteState* fwState = (FileWriteState*)closure;
|
|
#ifdef MEM_DEBUG
|
|
CEAppGlobals* globals = fwState->globals;
|
|
#endif
|
|
XP_UCHAR* path = fwState->path;
|
|
XP_U16 len = (XP_U16)XP_STRLEN( path ) + 1; /* 1: null byte */
|
|
wchar_t widebuf[len];
|
|
HANDLE fileH;
|
|
|
|
MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, path, len, widebuf, len );
|
|
|
|
fileH = CreateFile( widebuf, GENERIC_WRITE, 0, NULL,
|
|
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
|
|
|
|
if ( fileH != INVALID_HANDLE_VALUE ) {
|
|
XP_UCHAR* buf;
|
|
XP_U32 nWritten;
|
|
|
|
len = stream_getSize( stream );
|
|
buf = XP_MALLOC( globals->mpool, len );
|
|
stream_getBytes( stream, buf, len );
|
|
|
|
WriteFile( fileH, buf, len, &nWritten, NULL );
|
|
SetEndOfFile( fileH );
|
|
CloseHandle( fileH );
|
|
|
|
XP_FREE( globals->mpool, buf );
|
|
}
|
|
} /* ceWriteToFile */
|
|
|
|
static XP_Bool
|
|
isDefaultName( CEAppGlobals* globals, const XP_UCHAR* name )
|
|
{
|
|
wchar_t path[CE_MAX_PATH_LEN];
|
|
(void)ceGetPath( globals, DEFAULT_GAME_PATH, path, VSIZE(path) );
|
|
return 0 == XP_STRCMP( path, name );
|
|
} /* isDefaultName */
|
|
|
|
static XP_Bool
|
|
ceSaveCurGame( CEAppGlobals* globals, XP_Bool autoSave )
|
|
{
|
|
XP_Bool confirmed = XP_FALSE;
|
|
/* If it doesn't yet have a name, get a path at which to save it. User
|
|
has a chance to cancel this. But if there is a name go ahead and save
|
|
using it and don't bother giving cancel chance -- since there's no
|
|
harm in making 'em restart. Not sure how this changes when IR's
|
|
involved. */
|
|
XP_UCHAR* name = globals->curGameName;
|
|
if ( name == NULL || isDefaultName( globals, name ) ) {
|
|
XP_UCHAR* newName = NULL;
|
|
|
|
if ( autoSave ) {
|
|
XP_U16 len;
|
|
wchar_t path[CE_MAX_PATH_LEN];
|
|
len = 1 + ceGetPath( globals, DEFAULT_GAME_PATH,
|
|
path, VSIZE(path) );
|
|
newName = XP_MALLOC( globals->mpool, len );
|
|
XP_MEMCPY( newName, path, len );
|
|
|
|
confirmed = XP_TRUE;
|
|
} else {
|
|
wchar_t nameBuf[MAX_PATH];
|
|
|
|
confirmed = ceConfirmUniqueName( globals, globals->hWnd,
|
|
IDS_SAVENAME, nameBuf,
|
|
VSIZE(nameBuf) );
|
|
if ( confirmed ) {
|
|
XP_U16 len = wcslen(nameBuf);
|
|
newName = XP_MALLOC( globals->mpool, len + 1 );
|
|
WideCharToMultiByte( CP_ACP, 0, nameBuf, len + 1,
|
|
newName, len + 1, NULL, NULL );
|
|
}
|
|
}
|
|
|
|
if ( confirmed ) {
|
|
XP_ASSERT( !!newName );
|
|
if ( !!globals->curGameName ) {
|
|
XP_FREE( globals->mpool, globals->curGameName );
|
|
}
|
|
globals->curGameName = newName;
|
|
}
|
|
} else {
|
|
confirmed = XP_TRUE;
|
|
}
|
|
|
|
if ( confirmed ) {
|
|
if ( !!globals->game.server ) {
|
|
XWStreamCtxt* memStream;
|
|
DictionaryCtxt* dict;
|
|
FileWriteState fwState;
|
|
const char* dictName;
|
|
XP_U8 flags;
|
|
|
|
fwState.path = globals->curGameName;
|
|
fwState.globals = globals;
|
|
board_hideTray( globals->game.board ); /* so won't be visible when
|
|
next opened */
|
|
memStream = mem_stream_make( MEMPOOL globals->vtMgr, &fwState, 0,
|
|
ceWriteToFile );
|
|
stream_open( memStream );
|
|
|
|
/* the dictionary */
|
|
dict = model_getDictionary( globals->game.model );
|
|
#ifdef STUBBED_DICT /* don't check this in!!! */
|
|
dictName = NULL;
|
|
#else
|
|
dictName = !!dict? dict_getName( dict ) : NULL;
|
|
#endif
|
|
flags = !!dictName? 0x01 : 0x00;
|
|
flags |= CE_GAMEFILE_VERSION << 1;
|
|
stream_putU8( memStream, flags );
|
|
|
|
if ( !!dictName ) {
|
|
stringToStream( memStream, dictName );
|
|
}
|
|
|
|
ce_draw_toStream( globals->draw, memStream );
|
|
|
|
game_saveToStream( &globals->game, &globals->gameInfo, memStream );
|
|
|
|
stream_destroy( memStream );
|
|
}
|
|
}
|
|
|
|
return confirmed;
|
|
} /* ceSaveCurGame */
|
|
|
|
static void
|
|
ceSaveAndExit( CEAppGlobals* globals )
|
|
{
|
|
(void)ceSaveCurGame( globals, XP_TRUE );
|
|
ceSavePrefs( globals );
|
|
DestroyWindow(globals->hWnd);
|
|
} /* ceSaveAndExit */
|
|
|
|
static void
|
|
closeGame( CEAppGlobals* globals )
|
|
{
|
|
game_dispose( &globals->game );
|
|
gi_disposePlayerInfo( MPPARM(globals->mpool) &globals->gameInfo );
|
|
|
|
if ( !!globals->curGameName ) {
|
|
XP_FREE( globals->mpool, globals->curGameName );
|
|
}
|
|
}
|
|
|
|
static void
|
|
freeGlobals( CEAppGlobals* globals )
|
|
{
|
|
XP_U16 ii;
|
|
MPSLOT;
|
|
|
|
MPASSIGN( mpool, globals->mpool );
|
|
|
|
draw_destroyCtxt( (DrawCtx*)globals->draw );
|
|
|
|
closeGame( globals );
|
|
|
|
#ifndef XWFEATURE_STANDALONE_ONLY
|
|
if ( !!globals->socketWrap ) {
|
|
ce_sockwrap_delete( globals->socketWrap );
|
|
globals->socketWrap = NULL;
|
|
}
|
|
WSACleanup();
|
|
#endif
|
|
|
|
if ( !!globals->vtMgr ) {
|
|
vtmgr_destroy( MPPARM(mpool) globals->vtMgr );
|
|
}
|
|
if ( !!globals->util.vtable ) {
|
|
XP_FREE( mpool, globals->util.vtable );
|
|
}
|
|
for ( ii = 0; ii < N_CACHED_PATHS; ++ii ) {
|
|
if ( !!globals->specialDirs[ii] ) {
|
|
XP_FREE( mpool, globals->specialDirs[ii] );
|
|
}
|
|
}
|
|
|
|
ceFreeResStrings( globals );
|
|
if ( globals->locInst != globals->hInst ) {
|
|
ceCloseResFile( globals->locInst );
|
|
}
|
|
if ( globals->langFileName != NULL ) {
|
|
XP_FREE( globals->mpool, globals->langFileName );
|
|
globals->langFileName = NULL;
|
|
}
|
|
|
|
XP_FREE( globals->mpool, globals );
|
|
mpool_destroy( mpool );
|
|
} /* freeGlobals */
|
|
|
|
#ifdef _WIN32_WCE
|
|
static HWND
|
|
makeCommandBar( HWND hwnd, HINSTANCE hInst )
|
|
{
|
|
SHMENUBARINFO mbi;
|
|
|
|
XP_MEMSET( &mbi, 0, sizeof(mbi) );
|
|
mbi.cbSize = sizeof(mbi);
|
|
mbi.hwndParent = hwnd;
|
|
mbi.nToolBarId = IDM_MAIN_MENUBAR;
|
|
mbi.hInstRes = hInst;
|
|
/* Don't set dwFlags if you want the Wince5 two-button softkey menu. */
|
|
/* mbi.dwFlags = SHCMBF_HMENU; */
|
|
|
|
//mbi.dwFlags = SHCMBF_HIDESIPBUTTON; /* eeh added. Why??? */
|
|
|
|
if (!SHCreateMenuBar(&mbi)) {
|
|
/* will want to use this to change menubar: SHEnableSoftkey? */
|
|
XP_LOGF( "SHCreateMenuBar failed" );
|
|
return NULL;
|
|
}
|
|
|
|
return mbi.hwndMB;
|
|
} /* makeCommandBar */
|
|
#endif
|
|
|
|
#ifdef CEFEATURE_CANSCROLL
|
|
static XP_Bool
|
|
handleScroll( CEAppGlobals* globals, XP_S16 pos, /* only valid for THUMB* */
|
|
XP_S16 code, HWND wnd )
|
|
{
|
|
XP_Bool result = XP_FALSE;
|
|
|
|
if ( wnd == globals->scrollHandle ) {
|
|
XP_U16 curYOffset = board_getYOffset( globals->game.board );
|
|
XP_S16 newOffset = curYOffset;
|
|
|
|
XP_ASSERT( !!globals->game.board );
|
|
|
|
switch ( code ) {
|
|
/* case SB_BOTTOM: // Scrolls to the lower right */
|
|
/* case SB_ENDSCROLL: // Ends scroll */
|
|
|
|
case SB_LINEUP: // Scrolls one line up
|
|
case SB_PAGEUP: //
|
|
--newOffset;
|
|
break;
|
|
|
|
case SB_LINEDOWN: // Scrolls one line down
|
|
case SB_PAGEDOWN: // Scrolls one page down
|
|
++newOffset;
|
|
break;
|
|
|
|
case SB_THUMBTRACK: /* still dragging; don't redraw */
|
|
case SB_THUMBPOSITION:
|
|
newOffset = pos;
|
|
default:
|
|
break;
|
|
/* do nothing: leave newOffset == curYOffset */
|
|
}
|
|
|
|
result = curYOffset != newOffset
|
|
&& board_setYOffset( globals->game.board, newOffset );
|
|
}
|
|
return result;
|
|
} /* handleScroll */
|
|
#endif
|
|
|
|
static XP_Bool
|
|
ceFireTimer( CEAppGlobals* globals, XWTimerReason why )
|
|
{
|
|
XP_Bool draw = XP_FALSE;
|
|
XWTimerProc proc;
|
|
void* closure;
|
|
|
|
proc = globals->timerProcs[why];
|
|
if ( !!proc ) {
|
|
globals->timerProcs[why] = NULL;
|
|
closure = globals->timerClosures[why];
|
|
draw = (*proc)( closure, why );
|
|
/* Setting draw after firing timer allows scrolling to happen
|
|
while pen is held down. This is a hack. Perhaps having
|
|
the timer proc return whether drawing is needed would be
|
|
more consistent. */
|
|
} else {
|
|
XP_LOGF( "skipped timer; alread fired?" );
|
|
}
|
|
return draw;
|
|
} /* ceFireTimer */
|
|
|
|
/* WM_TIMER messages are low-priority. Hold a key down and key events will
|
|
* crowd it out of the queue so that the app doesn't see it until the key is
|
|
* released. There are more reliable timers, but they seem to require
|
|
* advanced techniques like semaphores. At least one article recommends
|
|
* polling over going to those lengths. This is better that polling. I hope
|
|
* it's enough.
|
|
*/
|
|
static XP_Bool
|
|
checkFireLateKeyTimer( CEAppGlobals* globals )
|
|
{
|
|
XP_Bool drop = XP_FALSE;
|
|
XWTimerReason whys[] = { TIMER_PENDOWN, TIMER_TIMERTICK
|
|
#if defined XWFEATURE_RELAY || defined COMMS_HEARTBEAT
|
|
, TIMER_COMMS
|
|
#endif
|
|
};
|
|
XP_U32 now = GetCurrentTime();
|
|
XP_U16 i;
|
|
|
|
for ( i = 0; i < sizeof(whys)/sizeof(whys[0]); ++i ) {
|
|
XWTimerReason why = whys[i];
|
|
if ( !!globals->timerProcs[why] ) {
|
|
if ( now >= globals->timerWhens[why] ) {
|
|
(void)ceFireTimer( globals, why );
|
|
drop = XP_TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return drop;
|
|
} /* checkFireLateKeyTimer */
|
|
|
|
static XP_Bool
|
|
checkPenDown( CEAppGlobals* globals )
|
|
{
|
|
XP_Bool draw = globals->penDown;
|
|
if ( draw ) {
|
|
draw = board_handlePenUp( globals->game.board, 0x7FFF, 0x7FFF );
|
|
globals->penDown = XP_FALSE;
|
|
}
|
|
return draw;
|
|
} /* checkPenDown */
|
|
|
|
#ifdef KEYBOARD_NAV
|
|
|
|
static XP_Bool
|
|
ceCheckHandleFocusKey( CEAppGlobals* globals, WPARAM wParam, LPARAM lParam,
|
|
XP_Bool keyDown, XP_Bool* handledP )
|
|
{
|
|
XP_Bool draw = XP_FALSE;
|
|
|
|
/* Sometimes, e.g. after a menu is released, we get KEY_UP not preceeded
|
|
by KEY_DOWN. Just drop those. */
|
|
if ( !keyDown && !globals->keyDown ) {
|
|
XP_LOGF( "%s: keyUp not preceeded by keyDown: dropping", __func__ );
|
|
} else {
|
|
XP_Bool isRepeat = keyDown && ((HIWORD(lParam) & KF_REPEAT) != 0);
|
|
XP_Key key;
|
|
XP_S16 incr = 0;
|
|
|
|
switch ( wParam ) {
|
|
case VK_UP:
|
|
key = XP_CURSOR_KEY_UP;
|
|
incr = -1;
|
|
break;
|
|
case VK_RIGHT:
|
|
key = XP_CURSOR_KEY_RIGHT;
|
|
incr = 1;
|
|
break;
|
|
case VK_DOWN:
|
|
key = XP_CURSOR_KEY_DOWN;
|
|
incr = 1;
|
|
break;
|
|
case VK_LEFT:
|
|
key = XP_CURSOR_KEY_LEFT;
|
|
incr = -1;
|
|
break;
|
|
case 0x0d:
|
|
case 0x5d: /* center key on WinMo5 Treo (at least) -- but also ']'*/
|
|
case VK_HOME:
|
|
key = XP_RETURN_KEY;
|
|
if ( isRepeat ) {
|
|
(void)checkFireLateKeyTimer( globals );
|
|
}
|
|
break;
|
|
|
|
/* Still need to produce these somehow */
|
|
/* XP_CURSOR_KEY_ALTRIGHT, */
|
|
/* XP_CURSOR_KEY_ALTUP, */
|
|
/* XP_CURSOR_KEY_ALTLEFT, */
|
|
/* XP_CURSOR_KEY_ALTDOWN, */
|
|
|
|
default:
|
|
key = XP_KEY_NONE;
|
|
break;
|
|
}
|
|
|
|
if ( key != XP_KEY_NONE ) {
|
|
BoardCtxt* board = globals->game.board;
|
|
|
|
if ( isRepeat ) {
|
|
draw = board_handleKeyRepeat( board, key, handledP );
|
|
} else if ( keyDown ) {
|
|
draw = board_handleKeyDown( board, key, handledP );
|
|
} else {
|
|
draw = board_handleKeyUp( board, key, handledP );
|
|
}
|
|
|
|
if ( !*handledP && incr != 0 && !keyDown ) {
|
|
BoardObjectType orderScroll[] = {
|
|
OBJ_SCORE, OBJ_BOARD, OBJ_NONE, OBJ_TRAY };
|
|
BoardObjectType orderNoScroll[] = {
|
|
OBJ_SCORE, OBJ_BOARD, OBJ_TRAY };
|
|
BoardObjectType* order;
|
|
XP_U16 orderLen;
|
|
BoardObjectType cur = board_getFocusOwner( board );
|
|
XP_U16 index = 0;
|
|
|
|
if ( !!globals->scrollHandle ) {
|
|
order = orderScroll;
|
|
orderLen = VSIZE(orderScroll);
|
|
} else {
|
|
order = orderNoScroll;
|
|
orderLen = VSIZE(orderNoScroll);
|
|
}
|
|
|
|
if ( !!globals->scrollHandle || (cur != OBJ_NONE) ) {
|
|
for ( ; ; ) {
|
|
if ( order[index] == cur ) {
|
|
break;
|
|
}
|
|
++index;
|
|
XP_ASSERT( index < orderLen );
|
|
}
|
|
index = (index + orderLen + incr) % orderLen;
|
|
}
|
|
draw = board_focusChanged( board, order[index], XP_TRUE );
|
|
|
|
if ( !!globals->scrollHandle ) {
|
|
XP_Bool scrollerHasFocus = globals->scrollerHasFocus;
|
|
if ( order[index] == OBJ_NONE ) {
|
|
XP_ASSERT( !scrollerHasFocus );
|
|
SetFocus( globals->scrollHandle );
|
|
scrollerHasFocus = XP_TRUE;
|
|
} else if ( scrollerHasFocus ) {
|
|
SetFocus( globals->hWnd );
|
|
scrollerHasFocus = XP_FALSE;
|
|
}
|
|
if ( scrollerHasFocus != globals->scrollerHasFocus ) {
|
|
globals->scrollerHasFocus = scrollerHasFocus;
|
|
#ifdef _WIN32_WCE
|
|
InvalidateRect( globals->hWnd, &globals->scrollRects[0], FALSE );
|
|
InvalidateRect( globals->hWnd, &globals->scrollRects[1], FALSE );
|
|
#else
|
|
InvalidateRect( globals->scrollHandle, NULL, FALSE );
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
globals->keyDown = keyDown;
|
|
return draw;
|
|
} /* ceCheckHandleFocusKey */
|
|
#endif /* KEYBOARD_NAV */
|
|
|
|
static void
|
|
ceToggleFullScreen( CEAppGlobals* globals )
|
|
{
|
|
globals->appPrefs.fullScreen = !globals->appPrefs.fullScreen;
|
|
|
|
ceSizeIfFullscreen( globals, globals->hWnd );
|
|
|
|
(void)cePositionBoard( globals );
|
|
} /* ceToggleFullScreen */
|
|
|
|
static void
|
|
doAbout( CEAppGlobals* globals )
|
|
{
|
|
wchar_t buf[1024];
|
|
(void)LoadString( globals->locInst, (UINT)IDS_ABOUT, buf, VSIZE(buf) );
|
|
assertOnTop( globals->hWnd );
|
|
MessageBox( globals->hWnd, buf, ceGetResStringL( globals, IDS_ABOUT_L ),
|
|
MB_OK | MB_ICONINFORMATION );
|
|
}
|
|
|
|
LRESULT CALLBACK
|
|
WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
LRESULT result = 0;
|
|
int wmId;
|
|
XP_Bool draw = XP_FALSE;
|
|
XWTimerReason why;
|
|
CEAppGlobals* globals;
|
|
XP_Bool handled = XP_FALSE;
|
|
XP_Bool callDefault = XP_FALSE;
|
|
|
|
if ( message == WM_CREATE ) {
|
|
globals = ((CREATESTRUCT*)lParam)->lpCreateParams;
|
|
SetWindowLongPtr( hWnd, GWL_USERDATA, (long)globals );
|
|
#ifdef _WIN32_WCE
|
|
globals->hwndCB = makeCommandBar( hWnd, globals->locInst );
|
|
#endif
|
|
|
|
#ifdef _WIN32_WCE
|
|
globals->sai.cbSize = sizeof(globals->sai);
|
|
#endif
|
|
} else {
|
|
/* XP_LOGF( "%s: event=%s (%d)", __func__, messageToStr(message), message ); */
|
|
globals = (CEAppGlobals*)GetWindowLongPtr( hWnd, GWL_USERDATA );
|
|
|
|
switch (message) {
|
|
|
|
#ifdef _WIN32_WCE
|
|
case WM_ACTIVATE:
|
|
// Notify shell of our activate message
|
|
SHHandleWMActivate( hWnd, wParam, lParam, &globals->sai, FALSE );
|
|
break;
|
|
|
|
case WM_SETTINGCHANGE:
|
|
SHHandleWMSettingChange( hWnd, wParam, lParam, &globals->sai );
|
|
if ( !!globals && !!globals->game.model ) {
|
|
cePositionBoard( globals );
|
|
board_invalAll( globals->game.board );
|
|
draw = XP_TRUE;
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
#ifdef CEFEATURE_CANSCROLL
|
|
# ifndef _WIN32_WCE
|
|
/* WM_CTLCOLORSCROLLBAR aren't delivered on CE. Some say can
|
|
* work around using WM_PAINT or WM_ERASEBKGND but no luck
|
|
* yet. */
|
|
case WM_CTLCOLORSCROLLBAR:
|
|
if ( (HWND)lParam == globals->scrollHandle ) {
|
|
if ( globals->scrollerHasFocus ) {
|
|
return (LRESULT)ce_draw_getFocusBrush( globals->draw );
|
|
}
|
|
}
|
|
break;
|
|
# endif
|
|
case WM_VSCROLL:
|
|
draw = checkPenDown( globals );
|
|
draw = handleScroll( globals, (short int)HIWORD(wParam),
|
|
(short int)LOWORD(wParam),
|
|
(HWND)lParam ) || draw;
|
|
break;
|
|
#endif
|
|
|
|
case WM_COMMAND:
|
|
(void)checkPenDown( globals );
|
|
wmId = LOWORD(wParam);
|
|
|
|
// Parse the menu selections:
|
|
switch (wmId) {
|
|
case ID_FILE_ABOUT:
|
|
doAbout( globals );
|
|
break;
|
|
case ID_GAME_GAMEINFO: {
|
|
GameInfoState state;
|
|
|
|
XP_MEMSET( &state, 0, sizeof(state) );
|
|
state.dlgHdr.globals = globals;
|
|
|
|
DialogBoxParam( globals->locInst, (LPCTSTR)IDD_GAMEINFO, hWnd,
|
|
(DLGPROC)GameInfo, (long)&state );
|
|
|
|
if ( !state.userCancelled ) {
|
|
if ( state.prefsChanged ) {
|
|
updateForColors( globals );
|
|
}
|
|
draw = server_do( globals->game.server );
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ID_FILE_NEWGAME:
|
|
XP_LOGF( "ID_FILE_NEWGAME" );
|
|
if ( ceSaveCurGame( globals, XP_FALSE )
|
|
|| queryBoxChar( globals, hWnd,
|
|
ceGetResString( globals,
|
|
IDS_OVERWRITE ) ) ) {
|
|
draw = ceDoNewGame( globals );
|
|
}
|
|
break;
|
|
|
|
case ID_FILE_SAVEDGAMES:
|
|
ceChooseAndOpen( globals );
|
|
break;
|
|
|
|
case ID_FILE_PREFERENCES:
|
|
ceDoPrefsDlg( globals );
|
|
break;
|
|
case ID_FILE_FULLSCREEN:
|
|
ceToggleFullScreen( globals );
|
|
break;
|
|
case ID_GAME_FINALSCORES:
|
|
if ( server_getGameIsOver( globals->game.server ) ) {
|
|
ceDisplayFinalScores( globals );
|
|
} else if ( queryBoxChar( globals, hWnd,
|
|
ceGetResString( globals,
|
|
IDS_ENDNOW ) ) ) {
|
|
server_endGame( globals->game.server );
|
|
draw = TRUE;
|
|
}
|
|
break;
|
|
|
|
case ID_GAME_TILECOUNTSANDVALUES:
|
|
ceCountsAndValues( globals );
|
|
break;
|
|
|
|
case ID_GAME_TILESLEFT:
|
|
ceTilesLeft( globals );
|
|
break;
|
|
|
|
case ID_GAME_HISTORY:
|
|
ceDoHistory( globals );
|
|
break;
|
|
|
|
case ID_MOVE_TRADE:
|
|
draw = handleTradeCmd( globals );
|
|
break;
|
|
case ID_MOVE_JUGGLE:
|
|
draw = handleJuggleCmd( globals );
|
|
break;
|
|
|
|
case ID_MOVE_HIDETRAY:
|
|
draw = handleHidetrayCmd( globals );
|
|
break;
|
|
case ID_MOVE_TURNDONE:
|
|
draw = handleDoneCmd( globals);
|
|
break;
|
|
|
|
case ID_MOVE_FLIP:
|
|
draw = board_flip( globals->game.board );
|
|
ceCheckMenus( globals );
|
|
break;
|
|
case ID_MOVE_VALUES:
|
|
draw = board_toggle_showValues( globals->game.board );
|
|
ceSetLeftSoftkey( globals, ID_MOVE_VALUES );
|
|
ceCheckMenus( globals );
|
|
break;
|
|
|
|
case ID_MOVE_HINT:
|
|
#ifdef XWFEATURE_SEARCHLIMIT
|
|
case ID_MOVE_LIMITEDHINT:
|
|
globals->askTrayLimits = wmId == ID_MOVE_LIMITEDHINT;
|
|
#endif
|
|
board_resetEngine( globals->game.board );
|
|
/* fallthru */
|
|
case ID_MOVE_NEXTHINT:
|
|
draw = ceHandleHintRequest( globals );
|
|
break;
|
|
|
|
case ID_FILE_EXIT:
|
|
ceSaveAndExit( globals ); /* autosaves; no user interaction */
|
|
break;
|
|
|
|
case ID_MOVE_UNDOCURRENT:
|
|
draw = board_replaceTiles( globals->game.board );
|
|
break;
|
|
|
|
case ID_MOVE_UNDOLAST:
|
|
draw = server_handleUndo( globals->game.server );
|
|
break;
|
|
|
|
default:
|
|
callDefault = XP_TRUE;
|
|
}
|
|
break;
|
|
case WM_PAINT:
|
|
if ( !!globals ) {
|
|
RECT winrect;
|
|
if ( GetUpdateRect( hWnd, &winrect, FALSE ) ) {
|
|
if ( !!globals->game.board ) {
|
|
XP_Rect xprect;
|
|
/* When an obscuring window goes away, the update region
|
|
needs to be redrawn. This allows invalidating it. */
|
|
|
|
RECTtoXPR( &xprect, &winrect );
|
|
board_invalRect( globals->game.board, &xprect );
|
|
|
|
XP_ASSERT( globals->hWnd == hWnd );
|
|
drawInsidePaint( globals, &winrect );
|
|
}
|
|
if ( !ValidateRect( hWnd, &winrect ) ) {
|
|
logLastError( "WM_PAINT:ValidateRect" );
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_LBUTTONDOWN:
|
|
draw = checkPenDown( globals );
|
|
globals->penDown = XP_TRUE;
|
|
draw = board_handlePenDown( globals->game.board, LOWORD(lParam),
|
|
HIWORD(lParam), &handled )
|
|
|| draw;
|
|
break;
|
|
|
|
case WM_MOUSEMOVE:
|
|
if ( globals->penDown ) {
|
|
draw = board_handlePenMove( globals->game.board,
|
|
LOWORD(lParam),
|
|
HIWORD(lParam) );
|
|
}
|
|
break;
|
|
|
|
case WM_LBUTTONUP:
|
|
if ( globals->penDown ) {
|
|
draw = board_handlePenUp( globals->game.board, LOWORD(lParam),
|
|
HIWORD(lParam) );
|
|
globals->penDown = XP_FALSE;
|
|
}
|
|
break;
|
|
|
|
#ifdef OVERRIDE_BACKKEY
|
|
/* Make the back key mean raise focus, but only if dived.
|
|
Otherwise allow the OS to do what it wants. Which means
|
|
exit? */
|
|
case WM_HOTKEY:
|
|
if ( (VK_TBACK == HIWORD(lParam)) && !!globals->game.board ) {
|
|
draw = board_handleKey( globals->game.board,
|
|
XP_RAISEFOCUS_KEY, &handled );
|
|
if ( !draw && !handled
|
|
/* Hack alert. Winders sends two WM_HOTKEY events per
|
|
press of the key. (lParam isn't well documented for
|
|
this event, but likely they're down and up.)
|
|
Unfiltered, the first raises focus and the second
|
|
exits the app. Bad. So we'll only raise if the
|
|
first was not handled. Note that this may well break
|
|
on devices I haven't tested on, later, whenever. */
|
|
&& (0 == (BACK_KEY_UP_MAYBE & LOWORD(lParam))) ) {
|
|
XP_LOGF( "calling ceSaveAndExit for VK_TBACK" );
|
|
/* I'm actually exiting the app rather than minimize. As
|
|
it stands, minimizing means that even if I relaunch
|
|
the app and quit properly I can't delete the .exe,
|
|
suggesting that the minimized guy isn't getting
|
|
reassociated when I relaunch. Until I fix that
|
|
exiting is best.
|
|
*/
|
|
ceSaveAndExit( globals );
|
|
/* SHNavigateBack() is the right way to handle this, but
|
|
isn't available via cegcc yet. Others have suggested
|
|
this as well as ShowWindow( hWnd, SW_MINIMIZE );
|
|
or SetWindowPos( hWnd, &CWnd::wndBottom, 0, 0, 0, 0,
|
|
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE );
|
|
*/
|
|
}
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
#ifdef KEYBOARD_NAV
|
|
case WM_KEYDOWN:
|
|
case WM_KEYUP:
|
|
draw = ceCheckHandleFocusKey( globals, wParam, lParam,
|
|
message==WM_KEYDOWN, &handled );
|
|
break;
|
|
#endif
|
|
case WM_CHAR:
|
|
if ( wParam == 0x08 ) {
|
|
wParam = XP_CURSOR_KEY_DEL;
|
|
#ifdef KEYBOARD_NAV
|
|
} else if ( wParam == ' ' ) {
|
|
wParam = XP_RAISEFOCUS_KEY;
|
|
#endif
|
|
}
|
|
draw = board_handleKey( globals->game.board, wParam, &handled )
|
|
|| board_handleKey( globals->game.board, wParam - ('a'-'A'),
|
|
&handled );
|
|
break;
|
|
|
|
case WM_TIMER:
|
|
why = (XWTimerReason)wParam;
|
|
if ( why == TIMER_PENDOWN || why == TIMER_TIMERTICK
|
|
#if defined XWFEATURE_RELAY || defined COMMS_HEARTBEAT
|
|
|| why == TIMER_COMMS
|
|
#endif
|
|
) {
|
|
XP_ASSERT( why < NUM_TIMERS_PLUS_ONE );
|
|
|
|
/* Kill since they otherwise repeat, but kill before firing
|
|
as fired proc may set another. */
|
|
(void)KillTimer( hWnd, globals->timerIDs[why] );
|
|
|
|
draw = ceFireTimer( globals, why );
|
|
}
|
|
break;
|
|
|
|
#ifdef _WIN32_WCE
|
|
/* case WM_SETFOCUS: */
|
|
/* hC = ImmGetContext( hWnd ); */
|
|
/* globals->imeWasOpen = ImmGetOpenStatus( hC ); */
|
|
/* ImmSetOpenStatus( hC, TRUE ); */
|
|
/* ImmEscape( NULL, hC, IME_ESC_SET_MODE, (LPVOID)IM_SPELL ); */
|
|
/* break; */
|
|
/* case WM_KILLFOCUS: */
|
|
/* ImmSetOpenStatus( hC, globals->imeWasOpen ); */
|
|
/* break; */
|
|
|
|
/* The code above this point works to turn 12-key->text
|
|
translation on, but not to turn it off, so other apps wind up
|
|
with it on after Crosswords quits. The recommended code is
|
|
below, but includes constants not in the version of cegcc I'm
|
|
using. Need to look into upgrading, but that requires a lot
|
|
of changes. Post B2.... */
|
|
|
|
/* DWORD dwRes = SendMessage((HWND)wParam, WM_IME_REQUEST, IMR_ISIMEAWARE, 0); */
|
|
/* hC = ImmGetContext( hWnd ); */
|
|
/* if ( (dwRes & IMEAF_AWARE) == IMEAF_AWARE ) { */
|
|
/* ImmEscape( NULL, hC, IME_ESC_RETAIN_MODE_ICON, (LPVOID)TRUE); */
|
|
/* } */
|
|
/* ImmSetOpenStatus( hC, FALSE); */
|
|
/* } */
|
|
/* break; */
|
|
/* case WM_IME_REQUEST: */
|
|
/* if ( wParam == IMR_ISIMEAWARE ) { */
|
|
/* return IMEAF_AWARE; */
|
|
/* } */
|
|
/* break; */
|
|
#endif
|
|
|
|
case WM_DESTROY:
|
|
#ifdef _WIN32_WCE
|
|
CommandBar_Destroy(globals->hwndCB); /* supposedly not needed */
|
|
#endif
|
|
PostQuitMessage(0);
|
|
freeGlobals( globals );
|
|
break;
|
|
|
|
case XWWM_TIME_RQST:
|
|
draw = server_do( globals->game.server );
|
|
break;
|
|
|
|
case XWWM_REM_SEL:
|
|
ceTilesLeft( globals );
|
|
break;
|
|
|
|
#ifndef XWFEATURE_STANDALONE_ONLY
|
|
case XWWM_HOSTNAME_ARRIVED:
|
|
ce_sockwrap_hostname( globals->socketWrap, wParam, lParam );
|
|
break;
|
|
|
|
case XWWM_SOCKET_EVT:
|
|
draw = ce_sockwrap_event( globals->socketWrap, wParam, lParam );
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
callDefault = XP_TRUE;
|
|
}
|
|
}
|
|
|
|
if ( callDefault ) {
|
|
result = DefWindowProc(hWnd, message, wParam, lParam );
|
|
} else if ( draw ) {
|
|
/* This is stupid. We can't just say "draw" because windoze clips
|
|
drawing to the inval rect, and the board isn't set up to tell us
|
|
what its inval rect is. So we inval everything, and then when the
|
|
WM_PAINT message comes we inval the whole board because there's a
|
|
huge inval rect. Dumb. Need to figure out how to have the
|
|
methods in cedraw.c set the clip region to encompass the object
|
|
being drawn -- taking board's word for it -- or the intersection
|
|
of that with the actual clip rgn in the case where some window's
|
|
gone away and revealed a large rect board didn't know about. That
|
|
was the source of some trouble on Palm, and CE's so fast this
|
|
works. But it's stupid. */
|
|
RECT r = { 100, 100, 102, 102 };
|
|
InvalidateRect( globals->hWnd, &r, FALSE /* erase */ );
|
|
}
|
|
|
|
return result;
|
|
} /* WndProc */
|
|
|
|
// Mesage handler for the About box.
|
|
LRESULT CALLBACK
|
|
ceAbout(HWND hDlg, UINT message, WPARAM wParam, LPARAM XP_UNUSED(lParam))
|
|
{
|
|
switch (message) {
|
|
case WM_INITDIALOG:
|
|
return TRUE;
|
|
|
|
case WM_COMMAND:
|
|
if ((LOWORD(wParam) == IDOK) || (LOWORD(wParam) == IDCANCEL)) {
|
|
EndDialog(hDlg, LOWORD(wParam));
|
|
return TRUE;
|
|
}
|
|
break;
|
|
}
|
|
return FALSE;
|
|
} /* ceAbout */
|
|
|
|
static XP_Bool
|
|
ceMsgFromStream( CEAppGlobals* globals, XWStreamCtxt* stream,
|
|
const wchar_t* title, XP_U16 buttons, XP_Bool destroy )
|
|
{
|
|
/* It seems we want to use messagebox for everything on smartphone and
|
|
Windows, but not on PPC since it doesn't scroll and doesn't use
|
|
softkeys. Well, on Windows since there's no scrolling limit by
|
|
size */
|
|
XP_Bool saidYes;
|
|
XP_Bool useMB;
|
|
#ifdef _WIN32_WCE
|
|
useMB = IS_SMARTPHONE(globals);
|
|
#else
|
|
useMB = stream_getSize(stream) <= 256; /* arbitrary... */
|
|
#endif
|
|
if ( useMB ) {
|
|
int result = messageBoxStream( globals, stream, title, buttons );
|
|
saidYes = (IDOK == result) | (IDRETRY == result) | (IDYES == result);
|
|
} else {
|
|
StrBoxState state;
|
|
|
|
XP_MEMSET( &state, 0, sizeof(state) );
|
|
|
|
state.title = title;
|
|
state.stream = stream;
|
|
state.isQuery = (buttons & ~MB_ICONMASK) != MB_OK;
|
|
state.dlgHdr.globals = globals;
|
|
|
|
assertOnTop( globals->hWnd );
|
|
DialogBoxParam( globals->locInst, (LPCTSTR)IDD_STRBOX, globals->hWnd,
|
|
(DLGPROC)StrBox, (long)&state );
|
|
saidYes = state.result == IDOK;
|
|
}
|
|
|
|
if ( destroy ) {
|
|
stream_destroy( stream );
|
|
}
|
|
|
|
return saidYes;
|
|
} /* ceMsgFromStream */
|
|
|
|
static XP_UCHAR*
|
|
ceStreamToStrBuf( MPFORMAL XWStreamCtxt* stream )
|
|
{
|
|
XP_U16 len = stream_getSize( stream );
|
|
XP_UCHAR* buf = XP_MALLOC( mpool, len + 1 );
|
|
stream_getBytes( stream, buf, len );
|
|
buf[len] = '\0';
|
|
|
|
return buf;
|
|
} /* ceStreamToStrBuf */
|
|
|
|
static int
|
|
messageBoxStream( CEAppGlobals* globals, XWStreamCtxt* stream,
|
|
const wchar_t* title, XP_U16 buttons )
|
|
{
|
|
XP_UCHAR* buf = ceStreamToStrBuf( MPPARM(globals->mpool) stream );
|
|
int result;
|
|
|
|
result = ceMessageBoxChar( globals, buf, ceCurDictIsUTF8(globals),
|
|
title, buttons );
|
|
|
|
XP_FREE( globals->mpool, buf );
|
|
return result;
|
|
} /* messageBoxStream */
|
|
|
|
XP_Bool
|
|
queryBoxChar( CEAppGlobals* globals, HWND hWnd, const XP_UCHAR* msg )
|
|
{
|
|
wchar_t widebuf[128];
|
|
XP_U16 answer;
|
|
|
|
XP_U16 len = MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, msg, -1,
|
|
widebuf, VSIZE(widebuf) );
|
|
widebuf[len] = 0;
|
|
|
|
answer = MessageBox( hWnd, widebuf,
|
|
ceGetResStringL( globals, IDS_QUESTION_L ),
|
|
MB_YESNO | MB_ICONQUESTION );
|
|
return answer == IDOK || answer == IDYES;
|
|
} /* queryBoxChar */
|
|
|
|
static XP_Bool
|
|
ceQueryFromStream( CEAppGlobals* globals, XWStreamCtxt* stream )
|
|
{
|
|
return ceMsgFromStream( globals, stream,
|
|
ceGetResStringL( globals, IDS_QUESTION_L ),
|
|
MB_OKCANCEL | MB_ICONQUESTION, XP_FALSE );
|
|
} /* ceQueryFromStream */
|
|
|
|
static void
|
|
RECTtoXPR( XP_Rect* dest, const RECT* src )
|
|
{
|
|
dest->top = (short)src->top;
|
|
dest->left = (short)src->left;
|
|
dest->width = (short)(src->right - src->left);
|
|
dest->height = (short)(src->bottom - src->top);
|
|
} /* RECTtoXPR */
|
|
|
|
void
|
|
wince_assert( XP_UCHAR* XP_UNUSED_LOG(s), int XP_UNUSED_LOG(line),
|
|
const char* XP_UNUSED_LOG(fileName),
|
|
const char* XP_UNUSED_LOG(func) )
|
|
{
|
|
XP_WARNF( "ASSERTION FAILED %s: %s in file %s, line %d\n", s,
|
|
func, fileName, line );
|
|
} /* wince_assert */
|
|
|
|
#ifdef ENABLE_LOGGING
|
|
static void
|
|
makeTimeStamp( XP_UCHAR* timeStamp, XP_U16 XP_UNUSED_DBG(size) )
|
|
{
|
|
SYSTEMTIME st;
|
|
DWORD tid;
|
|
|
|
tid = GetCurrentThreadId();
|
|
|
|
GetLocalTime( &st );
|
|
sprintf( timeStamp, "<%lx>%d:%.2d:%.2d ", tid, st.wHour, st.wMinute,
|
|
st.wSecond );
|
|
XP_ASSERT( size > strlen(timeStamp) );
|
|
} /* makeTimeStamp */
|
|
|
|
void
|
|
wince_warnf(const XP_UCHAR* format, ...)
|
|
{
|
|
XP_UCHAR buf[256];
|
|
va_list ap;
|
|
XP_U16 slen;
|
|
|
|
va_start( ap, format );
|
|
vsnprintf( buf, sizeof(buf), format, ap );
|
|
va_end(ap);
|
|
|
|
wince_debugf( "%s", buf );
|
|
|
|
slen = strlen(buf)+1;
|
|
wchar_t widebuf[slen];
|
|
|
|
MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, buf, slen,
|
|
widebuf, slen );
|
|
|
|
MessageBox( NULL, widebuf, L"WARNF", MB_OK | MB_ICONHAND );
|
|
} /* wince_warnf */
|
|
|
|
void
|
|
wince_debugf(const XP_UCHAR* format, ...)
|
|
{
|
|
#ifdef XWFEATURE_RELAY
|
|
static HANDLE s_logMutex = NULL;
|
|
#endif
|
|
XP_UCHAR buf[256];
|
|
XP_UCHAR timeStamp[32];
|
|
XP_U16 nBytes;
|
|
XP_U32 nWritten;
|
|
HANDLE fileH;
|
|
va_list ap;
|
|
wchar_t* logFileName;
|
|
|
|
va_start( ap, format );
|
|
vsprintf( buf, format, ap );
|
|
va_end(ap);
|
|
|
|
/* Create logfile if necessary and write to it in ascii. If there are
|
|
multiple threads, protect with mutex. */
|
|
|
|
#ifdef XWFEATURE_RELAY
|
|
if ( s_logMutex == NULL ) {
|
|
s_logMutex = CreateMutex( NULL, FALSE, NULL );
|
|
}
|
|
if ( WaitForSingleObject( s_logMutex, 200L ) == WAIT_OBJECT_0 ) {
|
|
#endif
|
|
makeTimeStamp(timeStamp, sizeof(timeStamp));
|
|
|
|
#ifdef _WIN32_WCE
|
|
logFileName = L"\\My Documents\\" LCROSSWORDS_DIR L"\\xwDbgLog.txt";
|
|
#else
|
|
logFileName = L"xwDbgLog.txt";
|
|
#endif
|
|
fileH = CreateFile( logFileName,
|
|
GENERIC_WRITE, 0, NULL,
|
|
OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
|
|
|
|
#ifdef _WIN32_WCE_EMULATION
|
|
strcat( buf, "\n" );
|
|
#else
|
|
strcat( buf, XP_CR );
|
|
#endif
|
|
SetFilePointer( fileH, 0, 0, FILE_END );
|
|
#ifdef DEBUG_TS
|
|
nBytes = strlen( timeStamp );
|
|
WriteFile( fileH, timeStamp, nBytes, &nWritten, NULL );
|
|
#endif
|
|
nBytes = strlen( buf );
|
|
WriteFile( fileH, buf, nBytes, &nWritten, NULL );
|
|
CloseHandle( fileH );
|
|
#ifdef XWFEATURE_RELAY
|
|
ReleaseMutex( s_logMutex );
|
|
}
|
|
#endif
|
|
} /* wince_debugf */
|
|
#endif /* ENABLE_LOGGING */
|
|
|
|
XP_U16
|
|
wince_snprintf( XP_UCHAR* buf, XP_U16 len, const XP_UCHAR* format, ... )
|
|
{
|
|
va_list ap;
|
|
|
|
va_start( ap, format );
|
|
|
|
_vsnprintf( buf, len, format, ap );
|
|
|
|
/* FormatMessage( */
|
|
/* FORMAT_MESSAGE_FROM_STRING, */
|
|
/* format, */
|
|
/* 0, */
|
|
/* 0, // Default language */
|
|
/* (LPTSTR)buf, */
|
|
/* len, &ap ); */
|
|
|
|
va_end(ap);
|
|
|
|
return strlen(buf);
|
|
} /* wince_snprintf */
|
|
|
|
#if defined XWFEATURE_RELAY || defined XWFEATURE_BLUETOOTH
|
|
static XP_Bool
|
|
got_data_proc( XP_U8* data, XP_U16 len, void* closure )
|
|
{
|
|
CEAppGlobals* globals = (CEAppGlobals*)closure;
|
|
XWStreamCtxt* stream;
|
|
XP_Bool draw;
|
|
|
|
stream = make_generic_stream( globals );
|
|
stream_putBytes( stream, data, len );
|
|
|
|
XP_ASSERT( !!globals->game.comms );
|
|
if ( comms_checkIncomingStream( globals->game.comms, stream, NULL ) ) {
|
|
draw = server_receiveMessage( globals->game.server, stream );
|
|
}
|
|
stream_destroy( stream );
|
|
ce_util_requestTime( &globals->util );
|
|
|
|
return draw;
|
|
} /* got_data_proc */
|
|
#endif
|
|
|
|
#ifdef COMMS_HEARTBEAT
|
|
static void
|
|
ce_reset_proc( void* XP_UNUSED_STANDALONE(closure) )
|
|
{
|
|
LOG_FUNC();
|
|
}
|
|
#endif
|
|
|
|
#ifndef XWFEATURE_STANDALONE_ONLY
|
|
static XP_S16
|
|
ce_send_proc( const XP_U8* buf, XP_U16 len, const CommsAddrRec* addrp,
|
|
void* closure )
|
|
{
|
|
XP_S16 nSent = -1;
|
|
CEAppGlobals* globals = (CEAppGlobals*)closure;
|
|
CommsAddrRec addr;
|
|
LOG_FUNC();
|
|
|
|
if ( !addrp ) {
|
|
comms_getAddr( globals->game.comms, &addr );
|
|
addrp = &addr;
|
|
}
|
|
|
|
XP_ASSERT( !!addrp ); /* firing */
|
|
switch( addrp->conType ) {
|
|
#if defined XWFEATURE_RELAY || defined XWFEATURE_BLUETOOTH
|
|
case COMMS_CONN_IP_DIRECT:
|
|
break;
|
|
case COMMS_CONN_RELAY:
|
|
if ( !globals->socketWrap ) {
|
|
globals->socketWrap = ce_sockwrap_new( MPPARM(globals->mpool)
|
|
got_data_proc, globals );
|
|
}
|
|
|
|
nSent = ce_sockwrap_send( globals->socketWrap, buf, len, addrp );
|
|
break;
|
|
#endif
|
|
#ifdef XWFEATURE_SMS
|
|
case COMMS_CONN_SMS:
|
|
nSent = ce_sms_send( globals, buf, len, addrp );
|
|
break;
|
|
#endif
|
|
default:
|
|
XP_LOGF( "unexpected conType %d", addrp->conType );
|
|
XP_ASSERT( 0 );
|
|
}
|
|
|
|
return nSent;
|
|
} /* ce_send_proc */
|
|
|
|
static void
|
|
ce_send_on_close( XWStreamCtxt* stream, void* closure )
|
|
{
|
|
CEAppGlobals* globals = (CEAppGlobals*)closure;
|
|
|
|
XP_ASSERT( !!globals->game.comms );
|
|
comms_send( globals->game.comms, stream );
|
|
}
|
|
#endif
|
|
|
|
static VTableMgr*
|
|
ce_util_getVTManager( XW_UtilCtxt* uc )
|
|
{
|
|
CEAppGlobals* globals = (CEAppGlobals*)uc->closure;
|
|
return globals->vtMgr;
|
|
} /* ce_util_getVTManager */
|
|
|
|
static void
|
|
ce_util_userError( XW_UtilCtxt* uc, UtilErrID id )
|
|
{
|
|
XP_U16 resID = 0;
|
|
|
|
switch( id ) {
|
|
case ERR_TILES_NOT_IN_LINE:
|
|
resID = IDS_TILES_NOT_IN_LINE;
|
|
break;
|
|
case ERR_NO_EMPTIES_IN_TURN:
|
|
resID = IDS_NO_EMPTIES_IN_TURN;
|
|
break;
|
|
|
|
case ERR_TWO_TILES_FIRST_MOVE:
|
|
resID = IDS_TWO_TILES_FIRST_MOVE;
|
|
break;
|
|
case ERR_TILES_MUST_CONTACT:
|
|
resID = IDS_TILES_MUST_CONTACT;
|
|
break;
|
|
case ERR_NOT_YOUR_TURN:
|
|
resID = IDS_NOT_YOUR_TURN;
|
|
break;
|
|
case ERR_NO_PEEK_ROBOT_TILES:
|
|
resID = IDS_NO_PEEK_ROBOT_TILES;
|
|
break;
|
|
case ERR_CANT_TRADE_MID_MOVE:
|
|
resID = IDS_CANT_TRADE_MID_MOVE;
|
|
break;
|
|
case ERR_TOO_FEW_TILES_LEFT_TO_TRADE:
|
|
resID = IDS_TOO_FEW_TILES_LEFT_TO_TRADE;
|
|
break;
|
|
case ERR_CANT_UNDO_TILEASSIGN:
|
|
resID = IDS_CANT_UNDO_TILEASSIGN;
|
|
break;
|
|
|
|
case ERR_CANT_HINT_WHILE_DISABLED:
|
|
resID = IDS_CANT_HINT_WHILE_DISABLED;
|
|
break;
|
|
|
|
#ifndef XWFEATURE_STANDALONE_ONLY
|
|
case ERR_NO_PEEK_REMOTE_TILES:
|
|
resID = IDS_NO_PEEK_REMOTE_TILES;
|
|
break;
|
|
case ERR_REG_UNEXPECTED_USER:
|
|
resID = IDS_REG_UNEXPECTED_USER;
|
|
break;
|
|
case ERR_SERVER_DICT_WINS:
|
|
resID = IDS_SERVER_DICT_WINS;
|
|
break;
|
|
case ERR_REG_SERVER_SANS_REMOTE:
|
|
resID = IDS_REG_SERVER_SANS_REMOTE;
|
|
break;
|
|
#endif
|
|
|
|
#ifdef XWFEATURE_RELAY
|
|
case ERR_RELAY_BASE + XWRELAY_ERROR_TIMEOUT:
|
|
resID = IDS_XWRELAY_ERROR_TIMEOUT;
|
|
break;
|
|
case ERR_RELAY_BASE + XWRELAY_ERROR_HEART_YOU:
|
|
resID = IDS_ERROR_HEART_YOU;
|
|
break;
|
|
case ERR_RELAY_BASE + XWRELAY_ERROR_HEART_OTHER:
|
|
case ERR_RELAY_BASE + XWRELAY_ERROR_LOST_OTHER:
|
|
resID = IDS_XWRELAY_ERROR_HEART_OTHER;
|
|
break;
|
|
/* Same string as above for now */
|
|
/* resID = IDS_XWRELAY_ERROR_LOST_OTHER; */
|
|
/* break; */
|
|
#endif
|
|
|
|
default:
|
|
XP_WARNF( "unknown error code: %d", id );
|
|
break;
|
|
}
|
|
|
|
if ( 0 != resID ) {
|
|
CEAppGlobals* globals = (CEAppGlobals*)uc->closure;
|
|
const XP_UCHAR* message = ceGetResString( globals, resID );
|
|
ceOops( globals, message );
|
|
}
|
|
} /* ce_util_userError */
|
|
|
|
static XP_Bool
|
|
ce_util_userQuery( XW_UtilCtxt* uc, UtilQueryID id, XWStreamCtxt* stream )
|
|
{
|
|
const char* query = NULL;
|
|
CEAppGlobals* globals = (CEAppGlobals*)uc->closure;
|
|
|
|
switch( id ) {
|
|
case QUERY_COMMIT_TURN:
|
|
return ceQueryFromStream( globals, stream );
|
|
|
|
case QUERY_COMMIT_TRADE:
|
|
query = ceGetResString( globals, IDS_QUERY_TRADE );
|
|
assertOnTop( globals->hWnd );
|
|
return queryBoxChar( globals, globals->hWnd, query );
|
|
|
|
case QUERY_ROBOT_MOVE:
|
|
return ceMsgFromStream( globals, stream,
|
|
ceGetResStringL( globals, IDS_FYI_L),
|
|
MB_OK | MB_ICONINFORMATION, XP_FALSE );
|
|
|
|
case QUERY_ROBOT_TRADE:
|
|
messageBoxStream( globals, stream,
|
|
ceGetResStringL( globals, IDS_FYI_L),
|
|
MB_OK | MB_ICONINFORMATION);
|
|
break;
|
|
|
|
default:
|
|
XP_ASSERT(0);
|
|
}
|
|
|
|
return XP_FALSE;
|
|
} /* ce_util_userQuery */
|
|
|
|
static XWBonusType
|
|
ce_util_getSquareBonus( XW_UtilCtxt* uc, const ModelCtxt* XP_UNUSED(model),
|
|
XP_U16 col, XP_U16 row )
|
|
{
|
|
XP_U16 index;
|
|
|
|
CEAppGlobals* globals = (CEAppGlobals*)uc->closure;
|
|
|
|
if ( !globals->bonusInfo ) {
|
|
HRSRC rsrcH;
|
|
HGLOBAL globH;
|
|
|
|
rsrcH = FindResource( globals->hInst, MAKEINTRESOURCE(ID_BONUS_RES),
|
|
TEXT("BONS") );
|
|
if ( !!rsrcH ) {
|
|
globH = LoadResource( globals->hInst, rsrcH );
|
|
|
|
if ( !!globH ) {
|
|
globals->bonusInfo = (XP_U16*)globH;
|
|
/* We don't want to call DeleteObject here, but should when
|
|
the app closes. Or does Wince free up all memory
|
|
associated with a process when it closes? PENDING(eeh) */
|
|
// DeleteObject( globH );
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( col > 7 ) col = 14 - col;
|
|
if ( row > 7 ) row = 14 - row;
|
|
index = (row*8) + col;
|
|
|
|
if ( !globals->bonusInfo || (index >= 8*8) ) {
|
|
XP_ASSERT( 0 );
|
|
return (XWBonusType)BONUS_NONE;
|
|
} else {
|
|
/* This is probably a bit slow. Consider caching the resource in
|
|
memory with one bonus value per byte. */
|
|
XP_U16 value = globals->bonusInfo[index/4];
|
|
value >>= ((3 - (index % 4)) * 4);
|
|
return value & 0x0F;
|
|
}
|
|
} /* ce_util_getSquareBonus */
|
|
|
|
static XP_S16
|
|
ce_util_userPickTile( XW_UtilCtxt* uc, const PickInfo* pi,
|
|
XP_U16 playerNum,
|
|
const XP_UCHAR** texts, XP_U16 nTiles )
|
|
{
|
|
BlankDialogState state;
|
|
CEAppGlobals* globals = (CEAppGlobals*)uc->closure;
|
|
XP_MEMSET( &state, 0, sizeof(state) );
|
|
|
|
state.dlgHdr.globals = globals;
|
|
state.texts = texts;
|
|
state.nTiles = nTiles;
|
|
state.playerNum = playerNum;
|
|
state.pi = pi;
|
|
|
|
assertOnTop( globals->hWnd );
|
|
DialogBoxParam( globals->locInst, (LPCTSTR)IDD_ASKBLANK, globals->hWnd,
|
|
(DLGPROC)BlankDlg, (long)&state );
|
|
return state.result;
|
|
} /* ce_util_userPickTile */
|
|
|
|
static XP_Bool
|
|
ce_util_askPassword( XW_UtilCtxt* uc, const XP_UCHAR* name,
|
|
XP_UCHAR* buf, XP_U16* len )
|
|
{
|
|
PasswdDialogState state;
|
|
CEAppGlobals* globals = (CEAppGlobals*)uc->closure;
|
|
XP_MEMSET( &state, 0, sizeof(state) );
|
|
|
|
state.dlgHdr.globals = globals;
|
|
state.name = name;
|
|
state.buf = buf;
|
|
state.lenp = len;
|
|
|
|
assertOnTop( globals->hWnd );
|
|
DialogBoxParam( globals->locInst, (LPCTSTR)IDD_ASKPASS, globals->hWnd,
|
|
(DLGPROC)PasswdDlg, (long)&state );
|
|
|
|
return !state.userCancelled;
|
|
} /* ce_util_askPassword */
|
|
|
|
static void
|
|
ce_util_trayHiddenChange( XW_UtilCtxt* uc, XW_TrayVisState XP_UNUSED(newState),
|
|
XP_U16 nVisibleRows )
|
|
{
|
|
CEAppGlobals* globals = (CEAppGlobals*)uc->closure;
|
|
XP_U16 nHiddenRows;
|
|
|
|
#ifdef CEFEATURE_CANSCROLL
|
|
/* If there's a scrollbar, hide/show it. It wants to be
|
|
active/visible only when the tray is NOT hidden */
|
|
|
|
if ( !!globals->scrollHandle ) {
|
|
nHiddenRows = model_numRows( globals->game.model ) - nVisibleRows;
|
|
updateScrollInfo( globals, nHiddenRows );
|
|
}
|
|
#endif
|
|
ceCheckMenus( globals );
|
|
drawInsidePaint( globals, NULL );
|
|
} /* ce_util_trayHiddenChange */
|
|
|
|
static void
|
|
ce_util_yOffsetChange( XW_UtilCtxt* uc, XP_U16 XP_UNUSED(oldOffset),
|
|
XP_U16 newOffset )
|
|
{
|
|
#ifdef CEFEATURE_CANSCROLL
|
|
CEAppGlobals* globals = (CEAppGlobals*)uc->closure;
|
|
if ( !!globals->scrollHandle ) {
|
|
(void)SetScrollPos( globals->scrollHandle, SB_CTL, newOffset, XP_TRUE );
|
|
}
|
|
#endif
|
|
} /* ce_util_yOffsetChange */
|
|
|
|
static void
|
|
ce_util_turnChanged( XW_UtilCtxt* uc )
|
|
{
|
|
CEAppGlobals* globals = (CEAppGlobals*)uc->closure;
|
|
ceSetLeftSoftkey( globals, ID_MOVE_TURNDONE );
|
|
}
|
|
|
|
static void
|
|
ce_util_notifyGameOver( XW_UtilCtxt* uc )
|
|
{
|
|
CEAppGlobals* globals = (CEAppGlobals*)uc->closure;
|
|
drawInsidePaint( globals, NULL );
|
|
ceDisplayFinalScores( globals );
|
|
|
|
ceSetLeftSoftkey( globals, ID_FILE_NEWGAME );
|
|
} /* ce_util_notifyGameOver */
|
|
|
|
static XP_Bool
|
|
ce_util_hiliteCell( XW_UtilCtxt* XP_UNUSED(uc), XP_U16 XP_UNUSED(col),
|
|
XP_U16 XP_UNUSED(row) )
|
|
{
|
|
return XP_TRUE;
|
|
} /* ce_util_hiliteCell */
|
|
|
|
static XP_Bool
|
|
ce_util_engineProgressCallback( XW_UtilCtxt* XP_UNUSED(uc) )
|
|
{
|
|
return XP_TRUE;
|
|
} /* ce_util_engineProgressCallback */
|
|
|
|
static void
|
|
ce_util_setTimer( XW_UtilCtxt* uc, XWTimerReason why,
|
|
XP_U16 XP_UNUSED_STANDALONE(when), XWTimerProc proc,
|
|
void* closure )
|
|
{
|
|
CEAppGlobals* globals = (CEAppGlobals*)uc->closure;
|
|
XP_U32 timerID;
|
|
XP_U32 howLong;
|
|
|
|
XP_ASSERT( why < NUM_TIMERS_PLUS_ONE );
|
|
globals->timerProcs[why] = proc;
|
|
globals->timerClosures[why] = closure;
|
|
|
|
switch ( why ) {
|
|
case TIMER_PENDOWN:
|
|
howLong = 400;
|
|
break;
|
|
case TIMER_TIMERTICK:
|
|
howLong = 1000; /* 1 second */
|
|
break;
|
|
#if defined XWFEATURE_RELAY || defined COMMS_HEARTBEAT
|
|
case TIMER_COMMS:
|
|
howLong = when * 1000;
|
|
break;
|
|
#endif
|
|
default:
|
|
XP_ASSERT(0);
|
|
return;
|
|
}
|
|
|
|
globals->timerWhens[why] = GetCurrentTime() + howLong;
|
|
|
|
timerID = SetTimer( globals->hWnd, why, howLong, NULL);
|
|
|
|
globals->timerIDs[why] = timerID;
|
|
} /* ce_util_setTimer */
|
|
|
|
static XP_Bool
|
|
ce_util_altKeyDown( XW_UtilCtxt* XP_UNUSED(uc) )
|
|
{
|
|
return GetKeyState(VK_LSHIFT) < 0
|
|
|| GetKeyState(VK_RSHIFT) < 0;
|
|
}
|
|
|
|
static void
|
|
ce_util_requestTime( XW_UtilCtxt* uc )
|
|
{
|
|
CEAppGlobals* globals = (CEAppGlobals*)uc->closure;
|
|
|
|
PostMessage( globals->hWnd, XWWM_TIME_RQST, 0, 0 );
|
|
} /* palm_util_requestTime */
|
|
|
|
static XP_U32
|
|
ce_util_getCurSeconds( XW_UtilCtxt* XP_UNUSED(uc) )
|
|
{
|
|
/* This function is never called! */
|
|
XP_U32 ticks = GetCurrentTime();
|
|
ticks /= 1000;
|
|
LOG_RETURNF( "%ld", ticks );
|
|
return ticks;
|
|
/* return 0L; */
|
|
} /* ce_util_getCurSeconds */
|
|
|
|
static DictionaryCtxt*
|
|
ce_util_makeEmptyDict( XW_UtilCtxt* uc )
|
|
{
|
|
CEAppGlobals* globals = (CEAppGlobals*)uc->closure;
|
|
#ifdef STUBBED_DICT
|
|
return make_stubbed_dict( MPPARM_NOCOMMA(globals->mpool) );
|
|
#else
|
|
return ce_dictionary_make_empty( globals );
|
|
#endif
|
|
} /* ce_util_makeEmptyDict */
|
|
|
|
#ifdef XWFEATURE_RELAY
|
|
static XWStreamCtxt*
|
|
ce_util_makeStreamFromAddr( XW_UtilCtxt* uc, XP_PlayerAddr channelNo )
|
|
{
|
|
XWStreamCtxt* stream;
|
|
CEAppGlobals* globals = (CEAppGlobals*)uc->closure;
|
|
|
|
stream = make_generic_stream( globals );
|
|
stream_setOnCloseProc( stream, ce_send_on_close );
|
|
stream_setAddress( stream, channelNo );
|
|
|
|
return stream;
|
|
} /* ce_util_makeStreamFromAddr */
|
|
#endif
|
|
|
|
static const XP_UCHAR*
|
|
ce_util_getUserString( XW_UtilCtxt* uc, XP_U16 stringCode )
|
|
{
|
|
CEAppGlobals* globals = (CEAppGlobals*)uc->closure;
|
|
XP_U16 resID = 0;
|
|
const XP_UCHAR* result = NULL;
|
|
|
|
switch( stringCode ) {
|
|
case STRD_REMAINING_TILES_ADD:
|
|
resID = IDS_REMAINING_TILES_ADD;
|
|
break;
|
|
case STRD_UNUSED_TILES_SUB:
|
|
resID = IDS_UNUSED_TILES_SUB;
|
|
break;
|
|
case STR_BONUS_ALL:
|
|
resID = IDS_BONUS_ALL;
|
|
break;
|
|
case STRD_TURN_SCORE:
|
|
resID = IDS_TURN_SCORE;
|
|
break;
|
|
case STR_COMMIT_CONFIRM:
|
|
resID = IDS_COMMIT_CONFIRM;
|
|
break;
|
|
case STR_LOCAL_NAME:
|
|
resID = IDS_LOCAL_NAME;
|
|
break;
|
|
case STR_NONLOCAL_NAME:
|
|
resID = IDS_NONLOCAL_NAME;
|
|
break;
|
|
case STRD_TIME_PENALTY_SUB:
|
|
resID = IDS_TIME_PENALTY_SUB;
|
|
break;
|
|
|
|
case STRD_CUMULATIVE_SCORE:
|
|
resID = IDS_CUMULATIVE_SCORE;
|
|
break;
|
|
case STRS_MOVE_ACROSS:
|
|
resID = IDS_MOVE_ACROSS;
|
|
break;
|
|
case STRS_MOVE_DOWN:
|
|
resID = IDS_MOVE_DOWN;
|
|
break;
|
|
case STRS_TRAY_AT_START:
|
|
resID = IDS_TRAY_AT_START;
|
|
break;
|
|
|
|
case STRS_NEW_TILES:
|
|
resID = IDS_NEW_TILES;
|
|
break;
|
|
case STRSS_TRADED_FOR:
|
|
resID = IDS_TRADED_FOR;
|
|
break;
|
|
case STR_PASS:
|
|
resID = IDS_PASS;
|
|
break;
|
|
case STR_PHONY_REJECTED:
|
|
resID = IDS_PHONY_REJECTED;
|
|
break;
|
|
|
|
case STRD_ROBOT_TRADED:
|
|
resID = IDS_ROBOT_TRADED;
|
|
break;
|
|
case STR_ROBOT_MOVED:
|
|
resID = IDS_ROBOT_MOVED;
|
|
break;
|
|
case STR_REMOTE_MOVED:
|
|
resID = IDS_REMOTE_MOVED;
|
|
break;
|
|
|
|
case STR_PASSED:
|
|
resID = IDS_PASSED;
|
|
break;
|
|
case STRSD_SUMMARYSCORED:
|
|
resID = IDS_SUMMARYSCORED;
|
|
break;
|
|
case STRD_TRADED:
|
|
resID = IDS_TRADED;
|
|
break;
|
|
case STR_LOSTTURN:
|
|
resID = IDS_LOSTTURN;
|
|
break;
|
|
|
|
#ifndef XWFEATURE_STANDALONE_ONLY
|
|
case STR_LOCALPLAYERS:
|
|
resID = IDS_LOCALPLAYERS;
|
|
break;
|
|
#endif
|
|
case STR_TOTALPLAYERS:
|
|
resID = IDS_TOTALPLAYERS;
|
|
break;
|
|
|
|
case STRS_VALUES_HEADER:
|
|
resID = IDS_VALUES_HEADER;
|
|
break;
|
|
|
|
default:
|
|
XP_WARNF( "id for stringCode %d not found", stringCode );
|
|
break;
|
|
}
|
|
|
|
if ( 0 != resID ) {
|
|
result = ceGetResString( globals, resID );
|
|
} else {
|
|
XP_WARNF( "%s: no resource for id %d", __func__, stringCode );
|
|
result = "";
|
|
}
|
|
return result;
|
|
} /* ce_util_getUserString */
|
|
|
|
static void
|
|
ce_formatBadWords( BadWordInfo* bwi, XP_UCHAR buf[], XP_U16 bufsiz )
|
|
{
|
|
XP_U16 i;
|
|
|
|
for ( i = 0, buf[0] = '\0'; ; ) {
|
|
XP_UCHAR wordBuf[24];
|
|
sprintf( wordBuf, "\"%s\"", bwi->words[i] );
|
|
XP_ASSERT( strlen(wordBuf) < sizeof(wordBuf)-1 );
|
|
strncat( buf, wordBuf, bufsiz - 1 );
|
|
if ( ++i == bwi->nWords ) {
|
|
break;
|
|
}
|
|
bufsiz -= strlen( wordBuf );
|
|
strncat( buf, ", ", bufsiz - 1 );
|
|
bufsiz -= 2;
|
|
}
|
|
} /* ce_formatBadWords */
|
|
|
|
static XP_Bool
|
|
ce_util_warnIllegalWord( XW_UtilCtxt* uc, BadWordInfo* bwi,
|
|
XP_U16 XP_UNUSED(turn), XP_Bool turnLost )
|
|
{
|
|
CEAppGlobals* globals = (CEAppGlobals*)uc->closure;
|
|
XP_UCHAR wordsBuf[256];
|
|
XP_UCHAR msgBuf[256];
|
|
const XP_UCHAR* fmt;
|
|
XP_Bool isOk;
|
|
|
|
ce_formatBadWords( bwi, wordsBuf, sizeof(wordsBuf) );
|
|
fmt = ceGetResString( globals, IDS_WRDNOTFOUND );
|
|
snprintf( msgBuf, VSIZE(msgBuf), fmt, wordsBuf );
|
|
|
|
if ( turnLost ) {
|
|
XP_Bool isUTF8 = ceCurDictIsUTF8( globals );
|
|
ceMessageBoxChar( globals, msgBuf, isUTF8,
|
|
ceGetResStringL( globals, IDS_ILLEGALWRD_L ),
|
|
MB_OK | MB_ICONHAND );
|
|
isOk = XP_TRUE;
|
|
} else {
|
|
const XP_UCHAR* str = ceGetResString( globals, IDS_USEANYWAY );
|
|
XP_U16 len = strlen( msgBuf );
|
|
XP_SNPRINTF( &msgBuf[len], VSIZE(msgBuf)-len, " %s", str );
|
|
assertOnTop( globals->hWnd );
|
|
isOk = queryBoxChar( globals, globals->hWnd, msgBuf );
|
|
}
|
|
|
|
return isOk;
|
|
} /* ce_util_warnIllegalWord */
|
|
|
|
static void
|
|
ce_util_remSelected( XW_UtilCtxt* uc )
|
|
{
|
|
CEAppGlobals* globals = (CEAppGlobals*)uc->closure;
|
|
PostMessage( globals->hWnd, XWWM_REM_SEL, 0, 0 );
|
|
}
|
|
|
|
#ifndef XWFEATURE_STANDALONE_ONLY
|
|
static void
|
|
ce_util_addrChange( XW_UtilCtxt* uc,
|
|
const CommsAddrRec* oldAddr,
|
|
const CommsAddrRec* newAddr )
|
|
{
|
|
CEAppGlobals* globals = (CEAppGlobals*)uc->closure;
|
|
|
|
XP_LOGF( "%s: old: %s -> new: %s", __func__,
|
|
ConnType2Str( oldAddr->conType ),
|
|
ConnType2Str( newAddr->conType ) );
|
|
|
|
/* A lot more needs to be tested for and done here... */
|
|
if ( COMMS_CONN_NONE == newAddr->conType ) {
|
|
if ( !!globals->socketWrap ) {
|
|
ce_sockwrap_delete( globals->socketWrap );
|
|
globals->socketWrap = NULL;
|
|
}
|
|
}
|
|
|
|
XP_LOGF( "ce_util_addrChange called; DO SOMETHING." );
|
|
} /* ce_util_addrChange */
|
|
#endif
|
|
|
|
#ifdef XWFEATURE_SEARCHLIMIT
|
|
static XP_Bool
|
|
ce_util_getTraySearchLimits( XW_UtilCtxt* uc, XP_U16* min, XP_U16* max )
|
|
{
|
|
CEAppGlobals* globals = (CEAppGlobals*)uc->closure;
|
|
HintLimitsState hls;
|
|
|
|
XP_MEMSET( &hls, 0, sizeof(hls) );
|
|
|
|
hls.dlgHdr.globals = globals;
|
|
hls.min = *min;
|
|
hls.max = *max;
|
|
|
|
assertOnTop( globals->hWnd );
|
|
DialogBoxParam( globals->locInst, (LPCTSTR)IDD_ASKHINTLIMTS, globals->hWnd,
|
|
(DLGPROC)HintLimitsDlg, (long)&hls );
|
|
|
|
if ( !hls.cancelled ) {
|
|
*min = hls.min;
|
|
*max = hls.max;
|
|
}
|
|
|
|
return !hls.cancelled;
|
|
} /* ce_util_getTraySearchLimits */
|
|
#endif
|
|
|
|
#ifdef SHOW_PROGRESS
|
|
blah blah -- these are not in use
|
|
static void
|
|
ce_util_engineStarting( XW_UtilCtxt* uc )
|
|
{
|
|
} /* ce_util_engineStarting */
|
|
|
|
static void
|
|
ce_util_engineStopping( XW_UtilCtxt* uc )
|
|
{
|
|
} /* ce_util_engineStopping */
|
|
#endif
|