xwords/wince/cesvdgms.c
ehouse 8f08667f5d Lots more changes, and some reversions. Win32, gtk and ncurses all
work now including saving/opening games.  Network play probably
doesn't as writing/reading a dict from stream has to change.  It's
doubtful patches from this branch will be merged.  Rather, I'll take
the concepts and crib some code when re-implementing.  Concepts:
XP_UCHAR becomes UTF-8 on both platforms, and on wince we translate to
wchar_t just as now but from the utf-8 codepage.  That keeps the work
and risk to a minimum.
2009-03-29 15:53:15 +00:00

514 lines
16 KiB
C

/* -*- fill-column: 77; compile-command: "make TARGET_OS=wince DEBUG=TRUE" -*- */
/*
* Copyright 2004-2008 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.
*/
#include <windowsx.h>
#include "stdafx.h"
#include <commdlg.h>
#include <wchar.h>
#ifdef _win32_wce
#include <aygshell.h>
#endif
#include "cemain.h"
#include "cesvdgms.h"
#include "ceutil.h"
#include "cedebug.h"
#include "debhacks.h"
typedef struct CeSaveGameNameState {
CeDlgHdr dlgHdr;
wchar_t* buf;
XP_U16 buflen;
XP_U16 lableTextId;
XP_Bool cancelled;
XP_Bool inited;
} CeSaveGameNameState;
static XP_Bool
ceFileExists( CEAppGlobals* globals, const wchar_t* name )
{
wchar_t buf[CE_MAX_PATH_LEN];
DWORD attributes;
XP_U16 len;
len = ceGetPath( globals, DEFAULT_DIR_PATH_L, buf, VSIZE(buf) );
_snwprintf( &buf[len], VSIZE(buf)-len, L"%s.xwg", name );
attributes = GetFileAttributes( buf );
return attributes != 0xFFFFFFFF;
}
static void
makeUniqueName( CEAppGlobals* globals, wchar_t* buf, XP_U16 bufLen )
{
XP_U16 ii;
for ( ii = 1; ii < 100; ++ii ) {
#ifdef DEBUG
int len =
#endif
_snwprintf( buf, bufLen, L"Untitled%d", ii );
XP_ASSERT( len < bufLen );
if ( !ceFileExists( globals, buf ) ) {
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 LRESULT CALLBACK
SaveNameDlg( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam )
{
CeSaveGameNameState* state;
XP_U16 wid;
BOOL result = FALSE;
if ( message == WM_INITDIALOG ) {
SetWindowLongPtr( hDlg, GWL_USERDATA, lParam );
state = (CeSaveGameNameState*)lParam;
state->cancelled = XP_TRUE;
state->inited = XP_FALSE;
wchar_t buf[128];
LoadString( state->dlgHdr.globals->hInst, state->lableTextId,
buf, VSIZE(buf) );
(void)SetDlgItemText( hDlg, IDC_SVGN_SELLAB, buf );
ceDlgSetup( &state->dlgHdr, hDlg, DLG_STATE_TRAPBACK );
result = TRUE;
} else {
state = (CeSaveGameNameState*)GetWindowLongPtr( hDlg, GWL_USERDATA );
if ( !!state ) {
CEAppGlobals* globals = state->dlgHdr.globals;
if ( !state->inited ) {
state->inited = XP_TRUE;
(void)SetDlgItemText( hDlg, IDC_SVGN_EDIT, state->buf );
}
if ( ceDoDlgHandle( &state->dlgHdr, message, wParam, lParam) ) {
result = TRUE;
} else {
switch (message) {
case WM_COMMAND:
wid = LOWORD(wParam);
switch( wid ) {
case IDOK: {
wchar_t buf[128];
XP_U16 len;
(void)GetDlgItemText( hDlg, IDC_SVGN_EDIT, buf,
VSIZE(buf) );
if ( ceFileExists( globals, buf ) ) {
wchar_t widebuf[128];
_snwprintf( widebuf, VSIZE(widebuf),
L"File \"%s\" already exists.", buf );
result = MessageBox( hDlg, widebuf, L"Oops!",
MB_OK | MB_ICONHAND );
(void)SetDlgItemText( hDlg, IDC_SVGN_EDIT,
state->buf );
break;
}
len = ceGetPath( globals, DEFAULT_DIR_PATH_L,
state->buf, state->buflen );
_snwprintf( &state->buf[len], state->buflen - len,
L"%s.xwg", buf );
XP_LOGW( __func__, state->buf );
/* fallthru */
state->cancelled = XP_FALSE;
}
case IDCANCEL:
EndDialog(hDlg, wid);
result = TRUE;
break;
}
}
}
}
}
return result;
} /* SaveNameDlg */
XP_Bool
ceConfirmUniqueName( CEAppGlobals* globals, HWND hWnd, XP_U16 strId,
wchar_t* buf, XP_U16 buflen )
{
CeSaveGameNameState state;
LOG_FUNC();
makeUniqueName( globals, buf, buflen );
XP_MEMSET( &state, 0, sizeof(state) );
state.dlgHdr.globals = globals;
state.buf = buf;
state.buflen = buflen;
state.lableTextId = strId;
(void)DialogBoxParam( globals->hInst, (LPCTSTR)IDD_SAVENAMEDLG,
hWnd, (DLGPROC)SaveNameDlg, (long)&state );
XP_LOGW( __func__, buf );
return !state.cancelled;
} /* ceConfirmUniqueName */
typedef struct CeSavedGamesState {
CeDlgHdr dlgHdr;
wchar_t* buf;
XP_U16 buflen;
XP_S16 sel; /* index of game name currently selected */
XP_U16 openGameIndex; /* index of game that's open */
wchar_t openNameW[128];
wchar_t newNameW[MAX_PATH];
XP_U16 nItems;
XP_U16 gameListId;
XP_Bool inited;
XP_Bool relaunch;
SavedGamesResult result;
} CeSavedGamesState;
static void
ceBasename( wchar_t* buf, const wchar_t* path )
{
const wchar_t* ptr = path + wcslen(path);
const wchar_t* dot = NULL;
for ( ; ; ) {
if ( ptr == path ) {
break;
} else if ( *ptr == L'\\' ) {
++ptr;
break;
} else if ( !dot && *ptr == L'.' ) {
dot = ptr;
}
--ptr;
}
lstrcpy( buf, ptr );
if ( !!dot ) {
buf[dot-ptr] = 0; /* nuke extension */
}
} /* ceBasename */
/* Probably belongs as a utility */
static void
getComboText( CeSavedGamesState* state, wchar_t* buf, XP_U16* lenp )
{
HWND hDlg = state->dlgHdr.hDlg;
CEAppGlobals* globals = state->dlgHdr.globals;
XP_U16 id = state->gameListId;
XP_U16 sel = state->sel;
XP_U16 len;
len = SendDlgItemMessage( hDlg, id, GETLBTEXTLEN(globals), sel, 0L );
if ( len < *lenp ) {
(void)SendDlgItemMessage( hDlg, id, GETLBTEXT(globals), sel,
(LPARAM)buf );
} else {
XP_ASSERT( 0 );
}
*lenp = len;
} /* getComboText */
static void
getFullSelPath( CeSavedGamesState* state, wchar_t* buf, XP_U16 buflen )
{
XP_U16 len = ceGetPath( state->dlgHdr.globals,
DEFAULT_DIR_PATH_L, buf, buflen );
buflen -= len;
getComboText( state, &buf[len], &buflen );
lstrcat( buf, L".xwg" );
}
static void
setButtons( CeSavedGamesState* state )
{
XP_Bool curSelected = state->openGameIndex == state->sel;
XP_Bool haveItem = state->nItems > 0;
HWND hDlg = state->dlgHdr.hDlg;
ceEnOrDisable( hDlg, IDC_SVGM_OPEN, haveItem && !curSelected );
ceEnOrDisable( hDlg, IDC_SVGM_DUP, haveItem );
ceEnOrDisable( hDlg, IDC_SVGM_DEL, haveItem && !curSelected );
ceEnOrDisable( hDlg, IDC_SVGM_CHANGE, haveItem );
}
static void
initSavedGamesData( CeSavedGamesState* state )
{
HANDLE fileH;
HWND hDlg = state->dlgHdr.hDlg;
CEAppGlobals* globals = state->dlgHdr.globals;
WIN32_FIND_DATA data;
wchar_t path[CE_MAX_PATH_LEN];
XP_S16 sel;
XP_U16 ii;
XP_U16 nItems = 0;
XP_MEMSET( &data, 0, sizeof(data) );
ceGetPath( globals, DEFAULT_DIR_PATH_L, path, VSIZE(path) );
lstrcat( path, L"*.xwg" );
fileH = FindFirstFile( path, &data );
for ( ii = 0; fileH != INVALID_HANDLE_VALUE; ++ii ) {
XP_U16 len = wcslen( data.cFileName );
XP_ASSERT( data.cFileName[len-4] == L'.');
data.cFileName[len-4] = 0;
(void)SendDlgItemMessage( hDlg, state->gameListId,
ADDSTRING(globals),
0, (LPARAM)data.cFileName );
++nItems;
if ( !FindNextFile( fileH, &data ) ) {
XP_ASSERT( GetLastError() == ERROR_NO_MORE_FILES );
break;
}
}
state->nItems = nItems;
/* Now locate the open game and game we should select (which may
differ) */
sel = SendDlgItemMessage( hDlg, state->gameListId, FINDSTRINGEXACT(globals),
-1, (LPARAM)state->openNameW );
XP_ASSERT( sel >= 0 ); /* should always have this */
state->openGameIndex = sel;
sel = SendDlgItemMessage( hDlg,state->gameListId, FINDSTRINGEXACT(globals),
-1, (LPARAM)state->newNameW );
if ( sel < 0 ) {
sel = state->openGameIndex;
}
SendDlgItemMessage( hDlg, state->gameListId, SETCURSEL(globals), sel, 0 );
state->sel = sel;
setButtons( state );
} /* initSavedGamesData */
static XP_Bool
renameSelected( CeSavedGamesState* state )
{
wchar_t newPath[MAX_PATH];
XP_Bool confirmed = ceConfirmUniqueName( state->dlgHdr.globals, state->dlgHdr.hDlg,
IDS_RENAME, newPath, VSIZE(newPath) );
if ( confirmed ) {
/* If we're renaming the current game, we have to exit and let
calling code handle it. If we're renaming any other game, we can
do it here. */
if ( state->openGameIndex == state->sel ) {
_snwprintf( state->buf, state->buflen, L"%s", newPath );
state->result = CE_SVGAME_RENAME;
} else {
wchar_t curPath[MAX_PATH];
getFullSelPath( state, curPath, VSIZE(curPath) );
confirmed = MoveFile( curPath, newPath );
}
}
if ( confirmed ) {
ceBasename( state->newNameW, newPath );
} else {
state->newNameW[0] = 0;
}
return confirmed;
} /* renameSelected */
static XP_Bool
duplicateSelected( CeSavedGamesState* state )
{
wchar_t newPath[MAX_PATH];
XP_Bool confirmed;
confirmed = ceConfirmUniqueName( state->dlgHdr.globals, state->dlgHdr.hDlg,
IDS_DUPENAME, newPath, VSIZE(newPath) );
if ( confirmed ) {
wchar_t curPath[MAX_PATH];
getFullSelPath( state, curPath, VSIZE(curPath) );
confirmed = CopyFile( curPath, newPath, TRUE ); /* TRUE is what??? */
}
if ( confirmed ) {
ceBasename( state->newNameW, newPath );
} else {
state->newNameW[0] = 0;
}
return confirmed;
} /* duplicateSelected */
static XP_Bool
deleteSelected( CeSavedGamesState* state )
{
/* confirm first!!!! */
XP_Bool confirmed = queryBoxChar( state->dlgHdr.hDlg,
XP_L("Are you certain you want to delete the ")
"selected game? This action cannot be "
"undone.");
if ( confirmed ) {
wchar_t pathW[CE_MAX_PATH_LEN];
XP_U16 len = ceGetPath( state->dlgHdr.globals,
DEFAULT_DIR_PATH_L, pathW, VSIZE(pathW) );
XP_U16 remLen = VSIZE(pathW) - len;
getComboText( state, &pathW[len], &remLen );
wcscat( pathW, L".xwg" );
confirmed = DeleteFile( pathW );
if ( confirmed ) {
state->sel = -1;
}
}
return confirmed;
} /* deleteSelected */
static XP_Bool
tryGameChanged( CeSavedGamesState* state )
{
XP_S16 sel = SendDlgItemMessage( state->dlgHdr.hDlg, state->gameListId,
GETCURSEL(state->dlgHdr.globals), 0, 0L);
XP_Bool changing = sel >= 0 && state->sel != sel;
if ( changing ) {
state->sel = sel;
setButtons( state );
}
return changing;
} /* tryGameChanged */
static LRESULT CALLBACK
SavedGamesDlg( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam )
{
CeSavedGamesState* state;
BOOL result = FALSE;
if ( message == WM_INITDIALOG ) {
SetWindowLongPtr( hDlg, GWL_USERDATA, lParam );
state = (CeSavedGamesState*)lParam;
state->inited = XP_FALSE;
state->gameListId = LB_IF_PPC(state->dlgHdr.globals,IDC_SVGM_GAMELIST);
ceDlgSetup( &state->dlgHdr, hDlg, DLG_STATE_DONEONLY );
ceDlgComboShowHide( &state->dlgHdr, IDC_SVGM_GAMELIST );
result = TRUE;
} else {
state = (CeSavedGamesState*)GetWindowLongPtr( hDlg, GWL_USERDATA );
if ( !!state ) {
if ( !state->inited ) {
state->inited = XP_TRUE;
initSavedGamesData( state );
}
if ( ceDoDlgHandle( &state->dlgHdr, message, wParam, lParam) ) {
result = TRUE;
} else if ( WM_NOTIFY == message ) {
result = tryGameChanged( state );
} else if ( message == WM_COMMAND ) {
XP_U16 wid = LOWORD(wParam);
if ( CBN_SELCHANGE == HIWORD(wParam) ) {
if (state->gameListId == wid ) {
result = tryGameChanged( state );
}
} else if ( BN_CLICKED == HIWORD(wParam) ) {
switch( wid ) {
case IDC_SVGM_DUP:
state->relaunch = duplicateSelected( state );
break;
case IDC_SVGM_CHANGE:
state->relaunch = renameSelected( state );
break;
case IDC_SVGM_DEL:
state->relaunch = deleteSelected( state );
break;
case IDC_SVGM_OPEN: {
wchar_t buf[128];
XP_U16 len = VSIZE(buf);
getComboText( state, buf, &len );
len = ceGetPath( state->dlgHdr.globals,
DEFAULT_DIR_PATH_L, state->buf,
state->buflen );
_snwprintf( &state->buf[len], state->buflen - len,
L"%s.xwg", buf );
XP_LOGW( "returning", state->buf );
state->result = CE_SVGAME_OPEN;
}
/* fallthrough */
case IDOK: /* Done button */
EndDialog(hDlg, wid);
result = TRUE;
break;
}
if ( state->relaunch ) {
EndDialog( hDlg, wid );
result = TRUE;
}
}
}
}
}
return result;
} /* SavedGamesDlg */
SavedGamesResult
ceSavedGamesDlg( CEAppGlobals* globals, const XP_UCHAR* curPath,
wchar_t* buf, XP_U16 buflen )
{
CeSavedGamesState state;
LOG_FUNC();
XP_MEMSET( &state, 0, sizeof(state) ); /* sets cancelled */
state.dlgHdr.globals = globals;
state.buf = buf;
state.buflen = buflen;
state.sel = -1;
if ( !!curPath ) {
wchar_t widebuf[MAX_PATH];
XP_U16 len;
len = (XP_U16)XP_STRLEN( curPath );
MultiByteToWideChar( CP_UTF8, MB_PRECOMPOSED, curPath, len + 1,
widebuf, len + 1 );
ceBasename( state.openNameW, widebuf );
}
for ( ; ; ) {
state.relaunch = XP_FALSE;
state.result = CE_SVGAME_CANCEL;
assertOnTop( globals->hWnd );
(void)DialogBoxParam( globals->hInst, (LPCTSTR)IDD_SAVEDGAMESDLG,
globals->hWnd,
(DLGPROC)SavedGamesDlg, (long)&state );
if ( !state.relaunch || (state.result == CE_SVGAME_RENAME) ) {
break;
}
}
XP_LOGW( __func__, buf );
return state.result;
} /* ceSavedGamesDlg */