xwords/xwords4/wince/cemain.c
2005-01-04 15:11:00 +00:00

2108 lines
63 KiB
C
Executable file

/* -*-mode: C; fill-column: 77; c-basic-offset: 4; -*- */
/*
* Copyright 2002-2004 by Eric House (fixin@peak.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 <winuser.h>
#include <aygshell.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 "LocalizedStrIncludes.h"
#define MAX_LOADSTRING 100
/* #define PREFSFILENAME L"\\My Documents\\Personal\\.xwprefs" */
/* #define UNSAVEDGAMEFILENAME "\\My Documents\\Personal\\_newgame" */
#define DEFAULT_DIR_NAME L"\\My Documents\\Crosswords"
/* #define PREFSFILENAME L"\\My Documents\\Crosswords\\.xwprefs" */
#define PREFSFILENAME L"\\My Documents\\Crosswords\\xwprefs"
#define UNSAVEDGAMEFILENAME "\\My Documents\\Crosswords\\_newgame"
#ifdef MEM_DEBUG
# define MEMPOOL globals->mpool,
#else
# define MEMPOOL
#endif
typedef struct FileWriteState {
CEAppGlobals* globals;
XP_UCHAR* path;
} FileWriteState;
/* forward util function decls */
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,
ModelCtxt* model,
XP_U16 col, XP_U16 row );
static XP_S16 ce_util_userPickTile( XW_UtilCtxt* uc, PickInfo* pi,
XP_U16 playerNum,
XP_UCHAR4* 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, XP_Bool nowHidden );
static void ce_util_yOffsetChange( XW_UtilCtxt* uc, XP_U16 oldOffset,
XP_U16 newOffset );
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 );
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 );
#ifndef XWFEATURE_STANDALONE_ONLY
static XWStreamCtxt* ce_util_makeStreamFromAddr( XW_UtilCtxt* uc,
XP_U16 channelNo );
#endif
static 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 );
#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 void notImpl( CEAppGlobals* globals );
static void messageBoxChar( CEAppGlobals* globals, XP_UCHAR* str,
wchar_t* title );
static XP_Bool queryBoxChar( CEAppGlobals* globals, XP_UCHAR* msg );
static XP_Bool ceMsgFromStream( CEAppGlobals* globals, XWStreamCtxt* stream,
wchar_t* title, XP_Bool isQuery,
XP_Bool destroy );
static void RECTtoXPR( XP_Rect* dest, RECT* src );
static XP_Bool doNewGame( CEAppGlobals* globals, XP_Bool silent );
static XP_Bool ceSaveCurGame( CEAppGlobals* globals, XP_Bool autoSave );
static void updateForColors( CEAppGlobals* globals );
// Forward declarations of functions included in this code module:
ATOM MyRegisterClass (HINSTANCE, LPTSTR);
BOOL InitInstance (HINSTANCE, int);
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK About (HWND, UINT, WPARAM, LPARAM);
int WINAPI
WinMain( HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
MSG msg;
HACCEL hAccelTable;
// Perform application initialization:
if (!InitInstance (hInstance, nCmdShow)) {
return FALSE;
}
hAccelTable = LoadAccelerators(hInstance, (LPCTSTR)IDC_XWORDS4);
// Main message loop:
while (GetMessage(&msg, NULL, 0, 0)) {
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return msg.wParam;
}
//
// 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;
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = (WNDPROC) WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = sizeof(CEAppGlobals*);
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_XWORDS4));
wc.hCursor = 0;
wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = 0;
wc.lpszClassName = szWindowClass;
return RegisterClass(&wc);
}
#define N_TOOLBAR_BUTTONS 4
static void
addButtonsToCmdBar( CEAppGlobals* globals )
{
XP_Bool success;
XP_U16 i;
int index;
int cmds[N_TOOLBAR_BUTTONS] = { FLIP_BUTTON_ID, VALUE_BUTTON_ID,
HINT_BUTTON_ID, JUGGLE_BUTTON_ID };
int resIDs[N_TOOLBAR_BUTTONS] = { IDB_FLIPBUTTON, IDB_VALUESBUTTON,
IDB_HINTBUTTON, IDB_JUGGLEBUTTON };
TBBUTTON buttData = {
0, /*iBitmap; */
0, //FLIP_BUTTON_ID,
TBSTATE_ENABLED, /* state */
TBSTYLE_BUTTON,
0,
-1
};
for ( i = 0; i < N_TOOLBAR_BUTTONS; ++i ) {
index = CommandBar_AddBitmap(globals->hwndCB, globals->hInst,
resIDs[i], 1, 16, 16 );
buttData.iBitmap = index;
buttData.idCommand = cmds[i];
success = CommandBar_InsertButton( globals->hwndCB, -1, &buttData );
}
} /* addButtonsToCmdBar */
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_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_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;
#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 */
static XP_Bool
cePositionBoard( CEAppGlobals* globals )
{
XP_Bool erase = XP_FALSE;
XP_U16 nCols, leftEdge;
XP_U16 boardHeight, trayTop, scoreWidth;
XP_ASSERT( !!globals->game.model );
nCols = model_numCols( globals->game.model );
XP_ASSERT( nCols <= CE_MAX_ROWS );
leftEdge = CE_BOARD_LEFT_RH;
leftEdge += ((CE_MAX_ROWS-nCols)/2) * CE_BOARD_SCALEH;
board_setTimerLoc( globals->game.board, CE_TIMER_LEFT,
CE_TIMER_TOP, CE_TIMER_WIDTH, CE_TIMER_HEIGHT );
/* If there's no timer, set the scoreboard to include the timer's rect so
that the entire screen will be owned and erased as appropriate.
Otherwise that region never gets redrawn. */
scoreWidth = CE_TIMER_LEFT - CE_SCORE_LEFT;
if ( !globals->gameInfo.timerEnabled ) {
scoreWidth += CE_TIMER_WIDTH;
}
board_setPos( globals->game.board, leftEdge,
CE_BOARD_TOP, XP_FALSE );
board_setScale( globals->game.board, CE_BOARD_SCALEH, CE_BOARD_SCALEV );
board_setScoreboardLoc( globals->game.board, CE_SCORE_LEFT,
CE_SCORE_TOP, scoreWidth,
CE_SCORE_HEIGHT, XP_TRUE );
board_setShowColors( globals->game.board, globals->appPrefs.showColors );
board_setYOffset( globals->game.board, 0, XP_FALSE /* why bother */ );
board_prefsChanged( globals->game.board, &globals->appPrefs.cp );
/* figure location for the tray. If possible, make it smaller than the
ideal to avoid using a scrollbar. Also, note at this point whether a
scrollbar will be required. */
boardHeight = CE_BOARD_SCALEV * nCols;
trayTop = boardHeight + CE_BOARD_TOP + 1;
if ( trayTop < CE_TRAY_TOP ) {
trayTop = CE_TRAY_TOP;/* we want it this low even if not
necessary */
} else {
/* while ( trayTop > CE_TRAY_TOP_MAX ) { */
/* XP_DEBUGF( "dropping trayTop from %d\n", trayTop ); */
/* trayTop -= scale; */
/* } */
}
XP_DEBUGF( "set trayTop to %d", trayTop );
board_setTrayLoc( globals->game.board,
CE_TRAY_LEFT_RH,
trayTop,
CE_TRAY_SCALEH, CE_TRAY_SCALEV,
CE_DIVIDER_WIDTH );
/* setCtrlsForTray( globals ); */
/* drawFormButtons( globals ); */
server_prefsChanged( globals->game.server, &globals->appPrefs.cp );
return erase;
} /* cePositionBoard */
/* Set the title to be app-name COLON file-name. If there's no colon there
* now append colon and game name. Else if there is a color replace what
* follows it with the new name.
*/
static void
ceSetTitleFromName( CEAppGlobals* globals )
{
wchar_t widebuf[256];
wchar_t* colonPos;
XP_UCHAR* baseStart;
XP_UCHAR* gameName;
XP_U16 len = (XP_U16)SendMessage( globals->hWnd, WM_GETTEXT,
sizeof(widebuf), (long)widebuf );
colonPos = wcsstr( widebuf, L":" );
if ( colonPos == NULL ) {
wcscat( widebuf, L":" );
colonPos = widebuf + len; /* we'll write at the end */
}
++colonPos; /* skip the colon */
gameName = globals->curGameName;
baseStart = strrchr( gameName, '\\' );
++baseStart;
len = (XP_U16)XP_STRLEN( baseStart );
MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, baseStart, len + 1,
colonPos, len + 1 );
/* now get rid of the ".xwg" */
colonPos = wcsrchr( widebuf, '.' );
if ( colonPos != NULL ) {
*colonPos = 0;
}
SendMessage( globals->hWnd, WM_SETTEXT, 0, (long)widebuf );
} /* ceSetTitleFromName */
static void
ceInitAndStartBoard( CEAppGlobals* globals, XP_Bool newGame, CeGamePrefs* gp )
{
DictionaryCtxt* dict;
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 ) {
XP_UCHAR* curDictName = dict_getName( dict );
if ( !!newDictName &&
( !curDictName || 0 != strcmp( curDictName, newDictName ) ) ) {
dict_destroy( dict );
dict = NULL;
} else {
replaceStringIfDifferent( MEMPOOL &globals->gameInfo.dictName,
curDictName );
}
}
if ( !dict ) {
#ifdef STUBBED_DICT
dict = make_stubbed_dict( MPPARM_NOCOMMA(globals->mpool) );
#else
XP_ASSERT( !!newDictName );
XP_DEBUGF( "calling ce_dictionary_make" );
dict = ce_dictionary_make( globals, copyString( MEMPOOL newDictName ));
#endif
XP_ASSERT( !!dict );
model_setDictionary( globals->game.model, dict );
}
if ( newGame ) {
XP_U16 newGameID = 0;
game_reset( MEMPOOL &globals->game, &globals->gameInfo,
newGameID, &globals->appPrefs.cp, (TransportSend)NULL,
globals );
if ( !!gp ) {
globals->gameInfo.hintsNotAllowed = gp->hintsNotAllowed;
globals->gameInfo.robotSmartness = gp->robotSmartness;
}
}
XP_ASSERT( !!globals->game.board );
(void)cePositionBoard( globals );
board_invalAll( globals->game.board );
InvalidateRect( globals->hWnd, NULL, TRUE /* erase */ );
server_do( globals->game.server );
globals->isNewGame = FALSE;
} /* ceInitAndStartBoard */
#ifdef DEBUG
void
logLastError( XP_UCHAR* comment )
{
LPVOID lpMsgBuf;
DWORD lastErr = GetLastError();
XP_UCHAR msg[256];
XP_U16 len;
XP_U16 lenSoFar;
strcpy( msg, comment );
strcat( msg, ": " );
lenSoFar = strlen( msg );
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
lastErr,
0, // Default language
(LPTSTR) &lpMsgBuf,
0,
NULL
);
len = wcslen( lpMsgBuf );
if ( len >= sizeof(msg)-lenSoFar ) {
len = sizeof(msg) - lenSoFar - 1;
}
WideCharToMultiByte( CP_ACP, 0, lpMsgBuf, len + 1,
msg + lenSoFar, len + 1, NULL, NULL );
LocalFree( lpMsgBuf );
XP_LOGF( "system error: %s", msg );
} /* logLastError */
#endif
static void
ceSavePrefs( CEAppGlobals* globals )
{
HANDLE fileH;
fileH = CreateFile( PREFSFILENAME, GENERIC_WRITE, 0, NULL,
OPEN_ALWAYS, FILE_ATTRIBUTE_HIDDEN, NULL );
if ( fileH != INVALID_HANDLE_VALUE ) {
XP_U32 nWritten;
XP_U16 nameLen = 0;
XP_UCHAR* name = globals->curGameName;
if ( name != NULL ) {
nameLen = (XP_U16)XP_STRLEN( name );
}
SetFilePointer( fileH, 0, 0, FILE_BEGIN );
/* write prefs, including version num */
WriteFile( fileH, &globals->appPrefs, sizeof(globals->appPrefs),
&nWritten, NULL );
XP_DEBUGF( "sizeof(appPrefs) => %ld", sizeof( globals->appPrefs ) );
WriteFile( fileH, &nameLen, sizeof(nameLen), &nWritten, NULL );
WriteFile( fileH, name, nameLen, &nWritten, NULL );
SetEndOfFile( fileH ); /* truncate anything previously there */
XP_DEBUGF( "ceSavePrefs: prefs file written" );
} else {
XP_LOGF( "failed to create prefs file" );
}
} /* ceSavePrefs */
static XP_Bool
ceLoadPrefs( CEAppGlobals* globals )
{
XP_Bool result = XP_FALSE;
HANDLE fileH;
fileH = CreateFile( PREFSFILENAME, GENERIC_READ, 0, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
if ( fileH != INVALID_HANDLE_VALUE ) {
XP_U32 fileSize = GetFileSize( fileH, NULL );
XP_DEBUGF( "ceLoadPrefs: prefs file found" );
if ( fileSize >= sizeof( CEAppPrefs ) ) {
CEAppPrefs tmpPrefs;
XP_U32 bytesRead;
XP_U16 nameLen;
XP_UCHAR* name;
if ( ReadFile( fileH, &tmpPrefs, sizeof(tmpPrefs),
&bytesRead, NULL ) ) {
if ( tmpPrefs.versionFlags == CUR_CE_PREFS_FLAGS ) {
XP_MEMCPY( &globals->appPrefs, &tmpPrefs,
sizeof(globals->appPrefs) );
result = XP_TRUE;
ReadFile( fileH, &nameLen, sizeof(nameLen), &bytesRead, NULL );
name = XP_MALLOC( globals->mpool, nameLen + 1 );
ReadFile( fileH, name, nameLen, &bytesRead, NULL );
name[nameLen] = '\0';
globals->curGameName = name;
XP_DEBUGF( "loaded saved name: %s", name );
}
}
}
CloseHandle( fileH );
} else {
XP_LOGF( "ceLoadPrefs: prefs file not found" );
}
return result; /* none found */
} /* ceLoadPrefs */
static XWStreamCtxt*
make_generic_stream( CEAppGlobals* globals )
{
return mem_stream_make( MPPARM(globals->mpool) globals->vtMgr,
globals, 0, NULL );
} /* make_generic_stream */
static XWStreamCtxt*
fileToStream( CEAppGlobals* globals, XP_UCHAR* path )
{
XWStreamCtxt* stream = NULL;
HANDLE fileH;
wchar_t widebuf[257];
XP_U16 len;
XP_DEBUGF( "fileToStream" );
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 void
ceSaveGamePrefs( CEAppGlobals* globals, XWStreamCtxt* stream )
{
} /* ceSaveGamePrefs */
static void
ceLoadGamePrefs( CEAppGlobals* globals, XWStreamCtxt* stream )
{
XP_DEBUGF( "ceLoadGamePrefs" );
} /* ceLoadGamePrefs */
static XP_Bool
ceLoadSavedGame( CEAppGlobals* globals )
{
XP_Bool success = XP_FALSE;
XWStreamCtxt* stream;
XP_DEBUGF( "ceLoadSavedGame" );
stream = fileToStream( globals, globals->curGameName );
success = stream != NULL;
if ( success ) {
DictionaryCtxt* dict;
XP_Bool hasDict;
ceLoadGamePrefs( globals, stream );
hasDict = stream_getU8( stream );
if ( hasDict ) {
#ifdef STUBBED_DICT
XP_ASSERT(0); /* just don't do this!!!! */
#else
XP_UCHAR* name = stringFromStream( MPPARM(globals->mpool)
stream );
dict = ce_dictionary_make( globals, name );
#endif
} else {
dict = NULL;
}
XP_DEBUGF( "calling game_makeFromStream" );
game_makeFromStream( MEMPOOL stream, &globals->game,
&globals->gameInfo,
dict, &globals->util, globals->draw,
&globals->appPrefs.cp, ce_ir_send, globals );
stream_destroy( stream );
}
return success;
} /* ceLoadSavedGame */
#ifndef XWFEATURE_STANDALONE_ONLY
XP_S16
ce_ir_send( XP_U8* buf, XP_U16 len, CommsAddrRec* addr, void* closure )
{
XP_DEBUGF( "ce_ir_send called" );
return -1;
} /* ce_ir_send */
#endif
static void
colorsFromRsrc( CEAppGlobals* globals )
{
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;
XP_LOGF( "setting colors in globals" );
for ( i = 0; i < NUM_COLORS; ++i ) {
XP_U8 r = (XP_U8)*ptr++;
XP_U8 g = (XP_U8)*ptr++;
XP_U8 b = (XP_U8)*ptr++;
globals->appPrefs.colors[i] = RGB( r, g, b );
}
DeleteObject( globH );
} /* colorsFromRsrc */
static void
ceInitPrefs( CEAppGlobals* globals )
{
globals->appPrefs.versionFlags = CUR_CE_PREFS_FLAGS;
globals->appPrefs.showColors = XP_TRUE;
globals->appPrefs.cp.showBoardArrow = XP_TRUE;
globals->appPrefs.cp.showRobotScores = XP_FALSE;
colorsFromRsrc( globals );
} /* ceInitPrefs */
//
// 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)
{
HWND hWnd;
TCHAR szTitle[MAX_LOADSTRING]; // The title bar text
TCHAR szWindowClass[MAX_LOADSTRING]; // The window class name
CEAppGlobals* globals;
BOOL result = TRUE;
XP_Bool oldGameLoaded;
XP_Bool prevStateExists;
XP_Bool newDone = XP_FALSE;
XP_U16 len;
MPSLOT;
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
hWnd = FindWindow( szWindowClass, szTitle);
if ( hWnd ) {
SetForegroundWindow( (HWND)((ULONG) hWnd | 0x00000001) );
return FALSE;
}
(void)CreateDirectory( DEFAULT_DIR_NAME, 0 );
#ifdef MEM_DEBUG
mpool = mpool_make();
#endif
globals = (CEAppGlobals*)XP_MALLOC( mpool, sizeof(*globals) );
XP_DEBUGF( "" );
XP_DEBUGF( "globals created: 0x%lx", globals );
XP_MEMSET( globals, 0, sizeof(*globals) );
MPASSIGN( globals->mpool, mpool );
globals->vtMgr = make_vtablemgr( MPPARM_NOCOMMA(mpool) );
len = wcslen( DEFAULT_DIR_NAME );
len = (len + 1) * sizeof(globals->lastDefaultDir[0]);
globals->lastDefaultDir = XP_MALLOC( mpool, len );
XP_MEMCPY( globals->lastDefaultDir, DEFAULT_DIR_NAME, len );
globals->hInst = hInstance;
// Initialize global strings
MyRegisterClass(hInstance, szWindowClass);
hWnd = CreateWindow(szWindowClass, szTitle, WS_VISIBLE,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, NULL, NULL, hInstance, globals);
globals->hWnd = hWnd;
if (!hWnd) {
return FALSE;
}
if ( globals->hwndCB ) {
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);
}
ceInitUtilFuncs( globals );
/* choose one. If none found it's an error. */
#ifndef STUBBED_DICT
globals->gameInfo.dictName = ceLocateNthDict( MPPARM(mpool) 0 );
result = globals->gameInfo.dictName != NULL;
if ( !result ) {
messageBoxChar( globals, "Please install at least one Crosswords "
"dictionary.", L"Fatal error" );
return FALSE;
}
#endif
gi_initPlayerInfo( MPPARM(mpool) &globals->gameInfo, "Player %d" );
/* here's where we want to behave differently if there's saved state.
But that's a long ways off. */
prevStateExists = ceLoadPrefs( globals );
if ( !prevStateExists ) {
ceInitPrefs( globals );
}
/* 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, globals->draw, &globals->appPrefs.cp,
(TransportSend)NULL, globals );
newDone = doNewGame( globals, XP_TRUE ); /* calls ceInitAndStartBoard */
if ( !newDone ) {
result = FALSE;
}
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
if (globals->hwndCB) {
CommandBar_Show(globals->hwndCB, TRUE);
}
if ( result && !newDone ) {
ceInitAndStartBoard( globals, !oldGameLoaded, NULL );
}
return result;
} /* InitInstance */
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
&notDone );
globals->hintPending = notDone;
return draw;
} /* ceHandleHintRequest */
static XP_Bool
handleTradeCmd( CEAppGlobals* globals )
{
return board_beginTrade( globals->game.board );
} /* handleTradeCmd */
static XP_Bool
handleJuggleCmd( CEAppGlobals* globals )
{
return board_juggleTray( globals->game.board );
} /* handleJuggleCmd */
static XP_Bool
handleHidetrayCmd( CEAppGlobals* globals )
{
XW_TrayVisState curState = board_getTrayVisState( globals->game.board );
if ( curState == TRAY_REVEALED ) {
return board_hideTray( globals->game.board );
} else {
return board_showTray( globals->game.board );
}
} /* 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, L"Tile Counts and Values",
XP_FALSE, 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, L"Remaining tiles",
XP_FALSE, 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, L"Game history",
XP_FALSE, XP_TRUE );
} /* ceDoHistory */
static void
drawInsidePaint( HWND hWnd, CEAppGlobals* globals )
{
HDC hdc;
hdc = GetDC( hWnd );
if ( !hdc ) {
logLastError( "drawInsidePaint" );
} else {
HDC prevHDC = globals->hdc;
globals->hdc = hdc;
board_draw( globals->game.board );
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, L"Final scores",
XP_FALSE, XP_TRUE );
} /* ceDisplayFinalScores */
static XP_Bool
doNewGame( CEAppGlobals* globals, XP_Bool silent )
{
GameInfoState giState;
XP_Bool changed = XP_FALSE;
/* What happens if user cancels below? I'm hosed without a name, no?
PENDING */
if ( globals->curGameName != NULL ) {
XP_FREE( globals->mpool, globals->curGameName );
globals->curGameName = NULL;
}
XP_MEMSET( &giState, 0, sizeof(giState) );
giState.globals = globals;
giState.isNewGame = XP_TRUE;
DialogBoxParam( globals->hInst, (LPCTSTR)IDD_GAMEINFO, globals->hWnd,
(DLGPROC)GameInfo, (long)&giState );
if ( !giState.userCancelled
#ifndef STUBBED_DICT
&& ((XP_U16)XP_STRLEN(giState.newDictName) > 0)
#endif
) {
if ( giState.prefsChanged ) {
loadCurPrefsFromState( &globals->appPrefs, &globals->gameInfo,
&giState.prefsPrefs );
if ( giState.colorsChanged ) {
updateForColors( globals );
}
}
ceInitAndStartBoard( globals, XP_TRUE, NULL );
changed = XP_TRUE;
}
return changed;
} /* doNewGame */
static void
ceChooseAndOpen( CEAppGlobals* globals )
{
wchar_t path[256];
OPENFILENAME openFileStruct;
XP_MEMSET( &openFileStruct, 0, sizeof(openFileStruct) );
XP_MEMSET( path, 0, sizeof(path) );
openFileStruct.lStructSize = sizeof(openFileStruct);
openFileStruct.hwndOwner = globals->hWnd;
openFileStruct.lpstrFilter = L"Crosswords games" L"\0"
L"*.xwg" L"\0\0";
openFileStruct.Flags = OFN_FILEMUSTEXIST
| OFN_HIDEREADONLY
| OFN_PATHMUSTEXIST;
openFileStruct.lpstrFile = path;
openFileStruct.nMaxFile = sizeof(path)/sizeof(path[0]);
if ( GetOpenFileName( &openFileStruct ) ) {
XP_UCHAR* name;
XP_U16 len;
len = wcslen(path);
name = XP_MALLOC( globals->mpool, len + 1 );
WideCharToMultiByte( CP_ACP, 0, path, len + 1,
name, len + 1, NULL, NULL );
if ( globals->curGameName != NULL
&& 0 == XP_STRCMP( name, globals->curGameName ) ){ /*already open*/
XP_FREE( globals->mpool, name );
} else if ( ceSaveCurGame( globals, XP_FALSE )
|| queryBoxChar( globals, "Do you really want to "
"overwrite the current game?" ) ) {
if ( globals->curGameName != NULL ) {
XP_FREE( globals->mpool, globals->curGameName );
}
globals->curGameName = name;
ceLoadSavedGame( globals );
ceInitAndStartBoard( globals, XP_FALSE, NULL );
ceSetTitleFromName( globals );
}
}
} /* ceChooseAndOpen */
static void
updateForColors( CEAppGlobals* globals )
{
ce_drawctxt_update( globals->draw, globals );
if ( !!globals->game.board ) {
board_invalAll( globals->game.board );
}
} /* updateForColors */
static void
ceDoPrefsDlg( CEAppGlobals* globals )
{
CePrefsDlgState state;
CePrefsPrefs prefsPrefs;
XP_MEMSET( &state, 0, sizeof(state) );
loadStateFromCurPrefs( &globals->appPrefs, &globals->gameInfo,
&prefsPrefs );
(void)WrapPrefsDialog( globals->hWnd, globals, &state, &prefsPrefs,
XP_FALSE );
if ( !state.userCancelled ) {
loadCurPrefsFromState( &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;
CEAppGlobals* globals = fwState->globals;
XP_UCHAR* path = fwState->path;
wchar_t widebuf[257];
XP_U16 len = (XP_U16)XP_STRLEN( path );
HANDLE fileH;
XP_DEBUGF( "ceWriteToFile called for %s", path );
len = (XP_U16)XP_STRLEN( path );
MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, path, len + 1,
widebuf, len + 1 );
fileH = CreateFile( widebuf, GENERIC_WRITE, 0, NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
if ( fileH != INVALID_HANDLE_VALUE ) {
XP_U16 len = stream_getSize( stream );
XP_UCHAR* buf = XP_MALLOC( globals->mpool, len );
XP_U32 nWritten;
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( XP_UCHAR* name )
{
return 0 == XP_STRCMP( UNSAVEDGAMEFILENAME, name, );
} /* isDefaultName */
static void
makeUniqueName( wchar_t* buf, XP_U16 bufLen )
{
XP_U16 i;
DWORD attributes;
for ( i = 1; i < 100; ++i ) {
swprintf( buf, DEFAULT_DIR_NAME L"\\Untitled%d.xwg", i );
XP_ASSERT( wcslen(buf) < bufLen );
attributes = GetFileAttributes( buf );
if ( attributes == 0xFFFFFFFF ) {
break;
}
}
/* If we fall out of the loop, the user will be asked to confirm delete
of Untitled99 or somesuch. That's ok.... */
} /* makeUniqueName */
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(name) ) {
wchar_t nameBuf[256];
XP_UCHAR* newName = NULL;
if ( autoSave ) {
XP_U16 len = XP_STRLEN(UNSAVEDGAMEFILENAME) + 1;
newName = XP_MALLOC( globals->mpool, len );
XP_MEMCPY( newName, UNSAVEDGAMEFILENAME, len );
confirmed = XP_TRUE;
} else {
OPENFILENAME sfs;
XP_MEMSET( &sfs, 0, sizeof(sfs) );
XP_MEMSET( nameBuf, 0, sizeof(nameBuf) );
makeUniqueName( nameBuf, sizeof(nameBuf)/sizeof(nameBuf[0]) );
sfs.lStructSize = sizeof(sfs);
sfs.Flags = OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT;
sfs.hwndOwner = globals->hWnd;
sfs.lpstrFile = nameBuf;
sfs.nMaxFile = sizeof(nameBuf)/sizeof(nameBuf[0]);
sfs.lpstrDefExt = L"xwg";
// sfs.lpstrTitle: doesn't work
// sfs.lpstrInitialDir: doesn't either
confirmed = GetSaveFileName( &sfs );
if ( confirmed ) {
XP_U16 len = wcslen(nameBuf);
XP_DEBUGF( "len(nameBuf) = %d", len );
newName = XP_MALLOC( globals->mpool, len + 1 );
WideCharToMultiByte( CP_ACP, 0, nameBuf, len + 1,
newName, len + 1, NULL, NULL );
/* If user picked from a different directory, remember it
as the new starting point. */
nameBuf[sfs.nFileOffset] = 0;
if ( wcscmp( nameBuf, globals->lastDefaultDir ) != 0 ) {
XP_FREE( globals->mpool, globals->lastDefaultDir );
globals->lastDefaultDir =
XP_MALLOC( globals->mpool,
(sfs.nFileOffset + 1) * 2 );
wcscpy( globals->lastDefaultDir, nameBuf );
}
}
}
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;
char* dictName;
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 );
ceSaveGamePrefs( globals, 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
stream_putU8( memStream, (XP_U8)!!dictName );
if ( !!dictName ) {
stringToStream( memStream, dictName );
}
game_saveToStream( &globals->game, &globals->gameInfo, memStream );
stream_destroy( memStream );
}
}
return confirmed;
} /* ceSaveCurGame */
static XP_Bool
ceConfirmAndSave( CEAppGlobals* globals )
{
XP_Bool confirmed = ceSaveCurGame( globals, XP_TRUE );
if ( confirmed ) {
ceSavePrefs( globals );
}
return confirmed;
} /* ceConfirmAndSave */
static HWND
makeCommandBar( HWND hwnd, HINSTANCE hInst )
{
SHMENUBARINFO mbi;
XP_MEMSET( &mbi, 0, sizeof(SHMENUBARINFO) );
mbi.cbSize = sizeof(SHMENUBARINFO);
mbi.hwndParent = hwnd;
mbi.nToolBarId = IDM_MENU;
mbi.hInstRes = hInst;
mbi.nBmpId = 0;
mbi.cBmpImages = 0;
mbi.dwFlags = SHCMBF_HIDESIPBUTTON; /* eeh added */
if (!SHCreateMenuBar(&mbi)) {
XP_LOGF( "SHCreateMenuBar failed" );
return NULL;
}
return mbi.hwndMB;
} /* makeCommandBar */
LRESULT CALLBACK
WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
RECT rt;
XP_Bool draw = XP_FALSE;
XWTimerReason why;
CEAppGlobals* globals;
XP_Bool handled;
if ( message == WM_CREATE ) {
globals = ((CREATESTRUCT*)lParam)->lpCreateParams;
SetWindowLong( hWnd, GWL_USERDATA, (long)globals );
globals->hwndCB = makeCommandBar( hWnd, globals->hInst );
addButtonsToCmdBar( globals );
} else {
globals = (CEAppGlobals*)GetWindowLong( hWnd, GWL_USERDATA );
switch (message) {
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 );
break;
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// Parse the menu selections:
switch (wmId) {
case ID_FILE_ABOUT:
DialogBox(globals->hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd,
(DLGPROC)About);
break;
case ID_GAME_GAMEINFO: {
GameInfoState state;
XP_MEMSET( &state, 0, sizeof(state) );
state.globals = globals;
state.isNewGame = XP_FALSE;
DialogBoxParam(globals->hInst, (LPCTSTR)IDD_GAMEINFO, hWnd,
(DLGPROC)GameInfo, (long)&state );
if ( !state.userCancelled && state.prefsChanged ) {
/* need to update some prefs? */
/* if ( state.colorsChanged ) { */
updateForColors( globals );
/* } */
}
}
break;
case ID_FILE_NEWGAME:
XP_LOGF( "ID_FILE_NEWGAME" );
if ( ceSaveCurGame( globals, XP_FALSE )
|| queryBoxChar( globals, "Do you really want to "
"overwrite the current game?" ) ) {
draw = doNewGame( globals, XP_FALSE );
}
break;
case ID_FILE_SAVEDGAMES:
ceChooseAndOpen( globals );
break;
case ID_FILE_PREFERENCES:
ceDoPrefsDlg( globals );
break;
case ID_GAME_FINALSCORES:
if ( server_getGameIsOver( globals->game.server ) ) {
ceDisplayFinalScores( globals );
} else if ( queryBoxChar( globals,
"Are you sure you want to end "
"the game now?" ) ) {
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:
case JUGGLE_BUTTON_ID:
draw = handleJuggleCmd( globals );
break;
case ID_MOVE_HIDETRAY:
draw = handleHidetrayCmd( globals );
break;
case ID_MOVE_TURNDONE:
draw = handleDoneCmd( globals);
break;
case FLIP_BUTTON_ID:
draw = board_flip( globals->game.board );
break;
case VALUE_BUTTON_ID:
draw = board_toggle_showValues( globals->game.board );
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:
case HINT_BUTTON_ID:
draw = ceHandleHintRequest( globals );
break;
case IDM_FILE_EXIT:
if ( ceConfirmAndSave( globals ) ) { /* user may cancel... */
DestroyWindow(hWnd);
}
break;
case ID_MOVE_UNDOCURRENT:
draw = board_replaceTiles( globals->game.board );
break;
case ID_MOVE_UNDOLAST:
draw = server_handleUndo( globals->game.server );
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_PAINT:
if ( !!globals && GetUpdateRect( hWnd, &rt, FALSE ) ) {
XP_Rect rect;
if ( !!globals->game.board ) {
/* When an obscuring window goes away, the update region
needs to be redrawn. This allows invalidating it. */
RECTtoXPR( &rect, &rt );
board_invalRect( globals->game.board, &rect );
drawInsidePaint( hWnd, globals );
}
if ( !ValidateRect( hWnd, &rt ) ) {
logLastError( "WM_PAINT:ValidateRect" );
}
}
break;
case WM_LBUTTONDOWN:
globals->penDown = XP_TRUE;
draw = board_handlePenDown( globals->game.board, LOWORD(lParam),
HIWORD(lParam), 0, &handled );
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), 0 );
globals->penDown = XP_FALSE;
}
break;
case WM_CHAR:
if ( wParam == 0x08 ) {
wParam = XP_CURSOR_KEY_DEL;
}
draw = board_handleKey( globals->game.board, wParam )
|| board_handleKey( globals->game.board, wParam - ('a'-'A') );
break;
case WM_TIMER:
why = (XWTimerReason)wParam;
if ( why == TIMER_PENDOWN || why == TIMER_TIMERTICK ) {
board_timerFired( globals->game.board, why );
/* they otherwise repeat.... */
XP_ASSERT( why <= N_TIMER_TYPES );
(void)KillTimer( hWnd, globals->timerIDs[why-1] );
}
break;
case WM_DESTROY:
CommandBar_Destroy(globals->hwndCB); /* supposedly not needed */
PostQuitMessage(0);
break;
case XWWM_TIME_RQST:
draw = server_do( globals->game.server );
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
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 0;
} /* WndProc */
// Mesage handler for the About box.
LRESULT CALLBACK
About(HWND hDlg, UINT message, WPARAM wParam, LPARAM 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;
} /* About */
static void
notImpl( CEAppGlobals* globals )
{
messageBoxChar( globals, "feature not implemented", NULL );
} /* notImpl */
static XP_Bool
ceMsgFromStream( CEAppGlobals* globals, XWStreamCtxt* stream,
wchar_t* title, XP_Bool isQuery, XP_Bool destroy )
{
StrBoxInit init;
init.title = title;
init.stream = stream;
init.isQuery = isQuery;
init.globals = globals;
DialogBoxParam( globals->hInst, (LPCTSTR)IDD_STRBOX, globals->hWnd,
(DLGPROC)StrBox, (long)&init );
if ( destroy ) {
stream_destroy( stream );
}
return init.result == IDOK;
} /* ceMsgFromStream */
static void
messageBoxChar( CEAppGlobals* globals, XP_UCHAR* str, wchar_t* title )
{
wchar_t* widebuf;
XP_U32 len, wsize;
/* first get the length required, then alloc and go */
len = MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, str, strlen(str),
NULL, 0 );
wsize = (len+1) * sizeof( wchar_t );
widebuf = XP_MALLOC( globals->mpool, wsize );
XP_MEMSET( widebuf, 0, wsize );
MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, str, strlen(str),
widebuf, len );
MessageBox( globals->hWnd, widebuf, title, MB_OK );
XP_FREE( globals->mpool, widebuf );
} /* messageBoxChar */
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 void
messageBoxStream( CEAppGlobals* globals, XWStreamCtxt* stream, wchar_t* title )
{
XP_UCHAR* buf = ceStreamToStrBuf( MPPARM(globals->mpool) stream );
messageBoxChar( globals, buf, title );
XP_FREE( globals->mpool, buf );
} /* messageBoxStream */
static XP_Bool
queryBoxChar( CEAppGlobals* globals, XP_UCHAR* msg )
{
wchar_t widebuf[128];
XP_U16 answer;
XP_U16 len = MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, msg, strlen(msg),
widebuf,
sizeof(widebuf)/sizeof(widebuf[0]) );
widebuf[len] = 0;
answer = MessageBox( globals->hWnd, widebuf, L"Question", MB_YESNO );
return answer == IDOK || answer == IDYES;
} /* queryBoxChar */
static XP_Bool
queryBoxStream( CEAppGlobals* globals, XWStreamCtxt* stream )
{
XP_UCHAR* buf = ceStreamToStrBuf( MPPARM(globals->mpool) stream );
XP_Bool result = queryBoxChar( globals, buf );
XP_FREE( globals->mpool, buf );
return result;
} /* queryBoxStream */
static XP_Bool
ceQueryFromStream( CEAppGlobals* globals, XWStreamCtxt* stream )
{
return ceMsgFromStream( globals, stream, L"Question", XP_TRUE,
XP_FALSE );
} /* ceQueryFromStream */
static void
RECTtoXPR( XP_Rect* dest, 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* s, int line, char* fileName )
{
XP_WARNF( "ASSERTION FAILED %s: file %s, line %d\n", s, fileName, line );
} /* wince_assert */
static void
makeTimeStamp( XP_UCHAR* timeStamp, XP_U16 size )
{
SYSTEMTIME st;
GetLocalTime( &st );
sprintf( timeStamp, "%d:%.2d:%.2d ", st.wHour, st.wMinute, st.wSecond );
XP_ASSERT( size > strlen(timeStamp) );
} /* makeTimeStamp */
void
wince_debugf(XP_UCHAR* format, ...)
{
XP_UCHAR buf[256];
/* wchar_t widebuf[256]; */
va_list ap;
va_start( ap, format );
vsprintf( buf, format, ap );
va_end(ap);
#if 0
messageBoxChar( globals, buf, NULL );
/* #elif defined _WIN32_WCE_EMULATION */
/* XP_MEMSET( widebuf, 0, sizeof(widebuf) ); */
/* MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, buf, strlen(buf), */
/* widebuf, sizeof(widebuf)/sizeof(widebuf[0]) ); */
/* OutputDebugString( widebuf ); */
#else
{
/* Create logfile if necessary and write to it in ascii. */
XP_U16 nBytes;
XP_U32 nWritten;
HANDLE fileH;
XP_UCHAR timeStamp[32];
makeTimeStamp(timeStamp, sizeof(timeStamp));
fileH = CreateFile( L"\\My Documents\\Crosswords\\xwDbgLog.txt",
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 );
nBytes = strlen( timeStamp );
WriteFile( fileH, timeStamp, nBytes, &nWritten, NULL );
XP_ASSERT( nWritten == nBytes );
nBytes = strlen( buf );
WriteFile( fileH, buf, nBytes, &nWritten, NULL );
XP_ASSERT( nWritten == nBytes );
CloseHandle( fileH );
}
#endif
} /* wince_debugf */
XP_U16
wince_snprintf( XP_UCHAR* buf, XP_U16 len, 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 */
/* I can't believe the stupid compiler's making me implement this */
void p_ignore(XP_UCHAR* c, ...){}
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_UCHAR* message;
CEAppGlobals* globals = (CEAppGlobals*)uc->closure;
switch( id ) {
case ERR_TILES_NOT_IN_LINE:
message = "All tiles played must be in a line.";
break;
case ERR_NO_EMPTIES_IN_TURN:
message = "Empty squares cannot separate tiles played.";
break;
case ERR_TWO_TILES_FIRST_MOVE:
message = "Must play two or more pieces on the first move.";
break;
case ERR_TILES_MUST_CONTACT:
message = "New pieces must contact others already in place (or "
"the middle square on the first move).";
break;
case ERR_NOT_YOUR_TURN:
message = "You can't do that; it's not your turn!";
break;
case ERR_NO_PEEK_ROBOT_TILES:
message = "No peeking at the robot's tiles!";
break;
case ERR_CANT_TRADE_MID_MOVE:
message = "Remove played tiles before trading.";
break;
case ERR_TOO_FEW_TILES_LEFT_TO_TRADE:
message = "Too few tiles left to trade.";
break;
case ERR_CANT_UNDO_TILEASSIGN:
message = "Tile assignment can't be undone.";
break;
default:
message = "unknown errorcode ID!!!";
break;
}
messageBoxChar( globals, message, L"Oops!" );
} /* ce_util_userError */
static XP_Bool
ce_util_userQuery( XW_UtilCtxt* uc, UtilQueryID id, XWStreamCtxt* stream )
{
char* query = NULL;
char* info = NULL;
CEAppGlobals* globals = (CEAppGlobals*)uc->closure;
XP_Bool answer = XP_FALSE;
XP_Bool queryWithStream = XP_FALSE;
switch( id ) {
case QUERY_COMMIT_TURN:
return ceQueryFromStream( globals, stream );
case QUERY_COMMIT_TRADE:
query = "Are you sure you want to spend this move trading tiles?";
return queryBoxChar( globals, query );
case QUERY_ROBOT_MOVE:
return ceMsgFromStream( globals, stream, L"FYI", XP_FALSE,
XP_FALSE );
case QUERY_ROBOT_TRADE:
messageBoxStream( globals, stream, L"FYI" );
break;
default:
XP_ASSERT(0);
}
return XP_FALSE;
} /* ce_util_userQuery */
static XWBonusType
ce_util_getSquareBonus( XW_UtilCtxt* uc, ModelCtxt* 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, PickInfo* pi,
XP_U16 playerNum,
XP_UCHAR4* texts, XP_U16 nTiles )
{
BlankDialogState state;
CEAppGlobals* globals = (CEAppGlobals*)uc->closure;
XP_MEMSET( &state, 0, sizeof(state) );
state.globals = globals;
state.texts = texts;
state.nTiles = nTiles;
state.playerNum = playerNum;
state.pi = pi;
DialogBoxParam( globals->hInst, (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.name = name;
state.buf = buf;
state.lenp = len;
DialogBoxParam( globals->hInst, (LPCTSTR)IDD_ASKPASS, globals->hWnd,
(DLGPROC)PasswdDlg, (long)&state );
return !state.userCancelled;
} /* ce_util_askPassword */
static void
ce_util_trayHiddenChange( XW_UtilCtxt* uc, XP_Bool nowHidden )
{
CEAppGlobals* globals = (CEAppGlobals*)uc->closure;
drawInsidePaint( globals->hWnd, globals );
} /* ce_util_trayHiddenChange */
static void
ce_util_yOffsetChange( XW_UtilCtxt* uc, XP_U16 oldOffset,
XP_U16 newOffset )
{
XP_ASSERT( XP_FALSE ); /* no scrolling on CE */
} /* ce_util_yOffsetChange */
static void
ce_util_notifyGameOver( XW_UtilCtxt* uc )
{
CEAppGlobals* globals = (CEAppGlobals*)uc->closure;
drawInsidePaint( globals->hWnd, globals );
ceDisplayFinalScores( globals );
} /* ce_util_notifyGameOver */
static XP_Bool
ce_util_hiliteCell( XW_UtilCtxt* uc, XP_U16 col, XP_U16 row )
{
return XP_TRUE;
} /* ce_util_hiliteCell */
static XP_Bool
ce_util_engineProgressCallback( XW_UtilCtxt* uc )
{
return XP_TRUE;
} /* ce_util_engineProgressCallback */
static void
ce_util_setTimer( XW_UtilCtxt* uc, XWTimerReason why )
{
CEAppGlobals* globals = (CEAppGlobals*)uc->closure;
XP_U32 timerID;
XP_U32 howLong;
if ( why == TIMER_PENDOWN ) {
howLong = 400;
} else if ( why == TIMER_TIMERTICK ) {
howLong = 1000; /* 1 second */
} else {
XP_ASSERT(0);
return;
}
timerID = SetTimer( globals->hWnd, why, howLong, NULL);
XP_ASSERT( why <= N_TIMER_TYPES );
globals->timerIDs[why-1] = timerID;
} /* ce_util_setTimer */
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* uc )
{
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( globals, NULL );
#endif
} /* ce_util_makeEmptyDict */
#ifndef XWFEATURE_STANDALONE_ONLY
static XWStreamCtxt*
ce_util_makeStreamFromAddr( XW_UtilCtxt* uc, XP_U16 channelNo )
{
} /* ce_util_makeStreamFromAddr */
#endif
static XP_UCHAR*
ce_util_getUserString( XW_UtilCtxt* uc, XP_U16 stringCode )
{
switch( stringCode ) {
case STRD_REMAINING_TILES_ADD:
return (XP_UCHAR*)"+ %d [all remaining tiles]";
case STRD_UNUSED_TILES_SUB:
return (XP_UCHAR*)"- %d [unused tiles]";
case STR_BONUS_ALL:
return (XP_UCHAR*)"Bonus for using all tiles: 50" XP_CR;
case STRD_TURN_SCORE:
return (XP_UCHAR*)"Score for turn: %d" XP_CR;
case STR_COMMIT_CONFIRM:
return (XP_UCHAR*)"Commit the current move?" XP_CR;
case STR_LOCAL_NAME:
return (XP_UCHAR*)"%s";
case STR_NONLOCAL_NAME:
return (XP_UCHAR*)"%s (remote)";
case STRD_TIME_PENALTY_SUB:
return (XP_UCHAR*)" - %d [time]";
case STRD_CUMULATIVE_SCORE:
return (XP_UCHAR*)"Cumulative score: %d" XP_CR;
case STRS_MOVE_ACROSS:
return (XP_UCHAR*)"move (from %s across)" XP_CR;
case STRS_MOVE_DOWN:
return (XP_UCHAR*)"move (from %s down)" XP_CR;
case STRS_TRAY_AT_START:
return (XP_UCHAR*)"Tray at start: %s" XP_CR;
case STRS_NEW_TILES:
return (XP_UCHAR*)"New tiles: %s" XP_CR;
case STRSS_TRADED_FOR:
return (XP_UCHAR*)"Traded %s for %s.";
case STR_PASS:
return (XP_UCHAR*)"pass" XP_CR;
case STR_PHONY_REJECTED:
return (XP_UCHAR*)"Illegal word in move; turn lost!" XP_CR;
case STRD_ROBOT_TRADED:
return (XP_UCHAR*)"Robot traded tiles %d this turn.";
case STR_ROBOT_MOVED:
return (XP_UCHAR*)"The robot made this move:" XP_CR;
case STR_REMOTE_MOVED:
return (XP_UCHAR*)"Remote player made this move:" XP_CR;
case STR_PASSED:
return (XP_UCHAR*)"Passed";
case STRSD_SUMMARYSCORED:
return (XP_UCHAR*)"%s:%d";
case STRD_TRADED:
return (XP_UCHAR*)"Traded %d";
case STR_LOSTTURN:
return (XP_UCHAR*)"Lost turn";
case STRS_VALUES_HEADER:
return (XP_UCHAR*)"%s counts/values:" XP_CR;
default:
XP_LOGF( "stringCode=%d", stringCode );
return (XP_UCHAR*)"unknown code";
}
} /* 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[18];
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 turn, XP_Bool turnLost )
{
CEAppGlobals* globals = (CEAppGlobals*)uc->closure;
XP_UCHAR wordsBuf[256];
XP_UCHAR msgBuf[256];
XP_Bool isOk;
ce_formatBadWords( bwi, wordsBuf, sizeof(wordsBuf) );
sprintf( msgBuf, "Word[s] %s not found in dictionary.", wordsBuf );
if ( turnLost ) {
messageBoxChar( globals, msgBuf, L"Illegal word" );
isOk = XP_TRUE;
} else {
strcat( msgBuf, " Use it anyway?" );
isOk = queryBoxChar( globals, msgBuf );
}
return isOk;
} /* ce_util_warnIllegalWord */
#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.globals = globals;
hls.min = *min;
hls.max = *max;
DialogBoxParam( globals->hInst, (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
static void
ce_util_engineStarting( XW_UtilCtxt* uc )
{
} /* ce_util_engineStarting */
static void
ce_util_engineStopping( XW_UtilCtxt* uc )
{
} /* ce_util_engineStopping */
#endif