mirror of
git://xwords.git.sourceforge.net/gitroot/xwords/xwords
synced 2024-12-27 09:58:45 +01:00
1b8e4223c1
are any installed and prefer one of them to English. Somebody wanting other than English shouldn't have to dig through menus to enable his language after installing the .dll.
475 lines
14 KiB
C
475 lines
14 KiB
C
/* -*- compile-command: "make -j3 TARGET_OS=wince DEBUG=TRUE"; -*- */
|
|
/*
|
|
* Copyright 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.
|
|
*/
|
|
|
|
#include "ceresstr.h"
|
|
#include "ceutil.h"
|
|
#include "cedebug.h"
|
|
#include "strutils.h"
|
|
|
|
static XP_U16 getDLLVersion( HINSTANCE hinst );
|
|
|
|
|
|
HINSTANCE
|
|
ceLoadResFile( const XP_UCHAR* file )
|
|
{
|
|
HINSTANCE hinst = NULL;
|
|
wchar_t widebuf[256];
|
|
(void)MultiByteToWideChar( CP_ACP, 0, file, -1, widebuf, VSIZE(widebuf) );
|
|
hinst = LoadLibrary( widebuf );
|
|
|
|
if ( CUR_DLL_VERSION != getDLLVersion( hinst ) ) {
|
|
FreeLibrary( hinst );
|
|
hinst = NULL;
|
|
}
|
|
|
|
return hinst;
|
|
} /* ceLoadResFile */
|
|
|
|
void
|
|
ceCloseResFile( HINSTANCE inst )
|
|
{
|
|
XP_ASSERT( !!inst );
|
|
FreeLibrary( inst );
|
|
}
|
|
|
|
#ifdef LOADSTRING_BROKEN
|
|
typedef struct _ResStrEntry {
|
|
union {
|
|
XP_UCHAR nstr[1];
|
|
wchar_t wstr[1];
|
|
} u;
|
|
} ResStrEntry;
|
|
|
|
typedef struct _ResStrStorage {
|
|
ResStrEntry* entries[CE_LAST_RES_ID - CE_FIRST_RES_ID + 1];
|
|
#ifdef DEBUG
|
|
XP_U16 nUsed;
|
|
#endif
|
|
} ResStrStorage;
|
|
|
|
static const ResStrEntry*
|
|
getEntry( CEAppGlobals* globals, XP_U16 resID, XP_Bool isWide )
|
|
{
|
|
ResStrStorage* storage = (ResStrStorage*)globals->resStrStorage;
|
|
ResStrEntry* entry;
|
|
XP_U16 index;
|
|
|
|
XP_ASSERT( resID >= CE_FIRST_RES_ID && resID <= CE_LAST_RES_ID );
|
|
index = CE_LAST_RES_ID - resID;
|
|
XP_ASSERT( index < VSIZE(storage->entries) );
|
|
|
|
if ( !storage ) {
|
|
XP_ASSERT( !globals->exiting );
|
|
storage = XP_MALLOC( globals->mpool, sizeof( *storage ) );
|
|
XP_MEMSET( storage, 0, sizeof(*storage) );
|
|
globals->resStrStorage = storage;
|
|
}
|
|
|
|
entry = storage->entries[index];
|
|
if ( !entry ) {
|
|
wchar_t wbuf[265];
|
|
XP_U16 len;
|
|
LoadString( globals->locInst, resID, wbuf, VSIZE(wbuf) );
|
|
if ( isWide ) {
|
|
len = wcslen( wbuf );
|
|
entry = (ResStrEntry*)XP_MALLOC( globals->mpool,
|
|
(len*sizeof(wchar_t))
|
|
+ sizeof(*entry) );
|
|
wcscpy( entry->u.wstr, wbuf );
|
|
} else {
|
|
XP_UCHAR nbuf[265];
|
|
(void)WideCharToMultiByte( CP_UTF8, 0, wbuf, -1,
|
|
nbuf, VSIZE(nbuf), NULL, NULL );
|
|
len = XP_STRLEN( nbuf );
|
|
entry = (ResStrEntry*)XP_MALLOC( globals->mpool,
|
|
len + 1 + sizeof(*entry) );
|
|
XP_STRNCPY( entry->u.nstr, nbuf, len + 1 );
|
|
}
|
|
|
|
storage->entries[index] = entry;
|
|
#ifdef DEBUG
|
|
++storage->nUsed;
|
|
#endif
|
|
}
|
|
|
|
return entry;
|
|
} /* getEntry */
|
|
#endif
|
|
|
|
const XP_UCHAR*
|
|
ceGetResString( CEAppGlobals* globals, XP_U16 resID )
|
|
{
|
|
#ifdef LOADSTRING_BROKEN
|
|
const ResStrEntry* entry = getEntry( globals, resID, XP_FALSE );
|
|
return entry->u.nstr;
|
|
#else
|
|
/* Docs say that you can call LoadString with 0 as the length and it'll
|
|
return a read-only ptr to the text within the resource, but I'm getting
|
|
a ptr to wide chars back the resource text being multibyte. I swear
|
|
I've seen it work, though, so might be a res file formatting thing or a
|
|
param to the res compiler. Need to investigate. Until I do, the above
|
|
caches local multibyte copies of the resources so the API can stay the
|
|
same. */
|
|
const XP_UCHAR* str = NULL;
|
|
LoadString( globals->locInst, resID, (LPSTR)&str, 0 );
|
|
return str;
|
|
#endif
|
|
}
|
|
|
|
const wchar_t*
|
|
ceGetResStringL( CEAppGlobals* globals, XP_U16 resID )
|
|
{
|
|
#ifdef LOADSTRING_BROKEN
|
|
const ResStrEntry* entry = getEntry( globals, resID, XP_TRUE );
|
|
return entry->u.wstr;
|
|
#else
|
|
/* Docs say that you can call LoadString with 0 as the length and it'll
|
|
return a read-only ptr to the text within the resource, but I'm getting
|
|
a ptr to wide chars back the resource text being multibyte. I swear
|
|
I've seen it work, though, so might be a res file formatting thing or a
|
|
param to the res compiler. Need to investigate. Until I do, the above
|
|
caches local multibyte copies of the resources so the API can stay the
|
|
same. */
|
|
const XP_UCHAR* str = NULL;
|
|
LoadString( globals->locInst, resID, (LPSTR)&str, 0 );
|
|
return str;
|
|
#endif
|
|
}
|
|
|
|
#ifdef LOADSTRING_BROKEN
|
|
void
|
|
ceFreeResStrings( CEAppGlobals* globals )
|
|
{
|
|
#ifdef DEBUG
|
|
XP_U16 nUsed = 0;
|
|
#endif
|
|
ResStrStorage* storage = (ResStrStorage*)globals->resStrStorage;
|
|
if ( !!storage ) {
|
|
XP_U16 ii;
|
|
for ( ii = 0; ii < VSIZE(storage->entries); ++ii ) {
|
|
ResStrEntry* entry = storage->entries[ii];
|
|
if ( !!entry ) {
|
|
XP_FREE( globals->mpool, entry );
|
|
#ifdef DEBUG
|
|
++nUsed;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
XP_ASSERT( nUsed == storage->nUsed );
|
|
XP_FREE( globals->mpool, storage );
|
|
globals->resStrStorage = NULL;
|
|
}
|
|
#ifdef DEBUG
|
|
XP_LOGF( "%s: %d of %d strings loaded and used", __func__, nUsed,
|
|
VSIZE(storage->entries) );
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
typedef struct _DllSelState {
|
|
CeDlgHdr dlgHdr;
|
|
wchar_t wbuf[MAX_PATH];
|
|
const wchar_t* curFile;
|
|
|
|
wchar_t* names[8];
|
|
wchar_t* files[8];
|
|
XP_U16 nItems;
|
|
XP_U16 initialSel;
|
|
|
|
XP_U16 dllListID;
|
|
XP_Bool inited;
|
|
XP_Bool cancelled;
|
|
} DllSelState;
|
|
|
|
static void
|
|
copyWideStr( CEAppGlobals* XP_UNUSED_DBG(globals),
|
|
wchar_t** dest, const wchar_t* src )
|
|
{
|
|
XP_U16 len = 1 + wcslen( src );
|
|
*dest = XP_MALLOC( globals->mpool, len * sizeof(**dest) );
|
|
wcscpy( *dest, src );
|
|
}
|
|
|
|
static XP_U16
|
|
getDLLVersion( HINSTANCE hinst )
|
|
{
|
|
XP_U16 version = 0; /* illegal value */
|
|
HRSRC rsrcH = FindResource( hinst, MAKEINTRESOURCE(ID_DLLVERS_RES),
|
|
TEXT("DLLV") );
|
|
if ( !!rsrcH ) {
|
|
HGLOBAL globH = LoadResource( hinst, rsrcH );
|
|
version = *(XP_U16*)globH;
|
|
DeleteObject( globH );
|
|
}
|
|
return version;
|
|
}
|
|
|
|
typedef XP_Bool (*DllCbk)( const WIN32_FIND_DATA* data, const wchar_t* name,
|
|
void* closure );
|
|
|
|
static void
|
|
for_each_dll( DllCbk cbk, void* closure )
|
|
{
|
|
wchar_t name[64];
|
|
HANDLE fileH;
|
|
WIN32_FIND_DATA data;
|
|
XP_MEMSET( &data, 0, sizeof(data) );
|
|
|
|
wchar_t path[MAX_PATH];
|
|
ceGetExeDir( path, VSIZE(path) );
|
|
wcscat( path, L"\\xwords4*.dll" );
|
|
|
|
fileH = FindFirstFile( path, &data );
|
|
while ( fileH != INVALID_HANDLE_VALUE ) {
|
|
|
|
HINSTANCE hinst = LoadLibrary( data.cFileName );
|
|
if ( !!hinst ) {
|
|
if ( CUR_DLL_VERSION != getDLLVersion( hinst ) ) {
|
|
/* do nothing; wrong version (or just not our .dll) */
|
|
} else if ( LoadString( hinst, IDS_LANGUAGE_NAME,
|
|
name, VSIZE(name) ) ) {
|
|
if ( !(*cbk)( &data, name, closure ) ) {
|
|
break;
|
|
}
|
|
} else {
|
|
XP_LOGF( "IDS_LANGUAGE_NAME not found in %ls",
|
|
data.cFileName );
|
|
}
|
|
FreeLibrary( hinst );
|
|
} else {
|
|
logLastError("LoadLibrary");
|
|
XP_LOGF( "Unable to open" );
|
|
}
|
|
|
|
if ( !FindNextFile( fileH, &data ) ) {
|
|
XP_ASSERT( GetLastError() == ERROR_NO_MORE_FILES );
|
|
break;
|
|
}
|
|
}
|
|
} /* for_each_dll */
|
|
|
|
typedef struct _ListingData {
|
|
DllSelState* state;
|
|
CEAppGlobals* globals;
|
|
XP_U16 nItems;
|
|
XP_S16 selIndex;
|
|
} ListingData;
|
|
|
|
static XP_Bool
|
|
addToListCB( const WIN32_FIND_DATA* data, const wchar_t* name, void* closure )
|
|
{
|
|
ListingData* ld = (ListingData*)closure;
|
|
|
|
(void)SendDlgItemMessage( ld->state->dlgHdr.hDlg, ld->state->dllListID,
|
|
ADDSTRING(ld->globals), 0, (LPARAM)name );
|
|
|
|
copyWideStr( ld->globals, &ld->state->names[ld->nItems], name );
|
|
copyWideStr( ld->globals, &ld->state->files[ld->nItems], data->cFileName );
|
|
|
|
if ( !!ld->state->curFile ) {
|
|
if ( !wcscmp( data->cFileName, ld->state->curFile ) ) {
|
|
ld->selIndex = ld->nItems;
|
|
}
|
|
}
|
|
|
|
++ld->nItems;
|
|
|
|
XP_Bool cont = ld->nItems < VSIZE(ld->state->names);
|
|
return cont;
|
|
}
|
|
|
|
|
|
/* Iterate through .dll files listing the name of any that has one. Pair with
|
|
* file from which it came since that's what we'll return.
|
|
*/
|
|
static void
|
|
listDlls( DllSelState* state )
|
|
{
|
|
HWND hDlg = state->dlgHdr.hDlg;
|
|
CEAppGlobals* globals = state->dlgHdr.globals;
|
|
wchar_t name[64];
|
|
|
|
ListingData ld = {
|
|
.globals = globals,
|
|
.state = state,
|
|
.nItems = 0,
|
|
.selIndex = 0
|
|
};
|
|
|
|
LoadString( globals->hInst, IDS_LANGUAGE_NAME, name, VSIZE(name) );
|
|
copyWideStr( globals, &state->names[ld.nItems++], name );
|
|
(void)SendDlgItemMessage( hDlg, state->dllListID, ADDSTRING(globals),
|
|
0, (LPARAM)name );
|
|
|
|
for_each_dll( addToListCB, &ld );
|
|
SendDlgItemMessage( hDlg, state->dllListID, SETCURSEL(globals),
|
|
ld.selIndex, 0L );
|
|
|
|
state->nItems = ld.nItems;
|
|
state->initialSel = ld.selIndex;
|
|
} /* listDlls */
|
|
|
|
static void
|
|
unlistDlls( DllSelState* state )
|
|
{
|
|
XP_U16 ii;
|
|
#ifdef DEBUG
|
|
CEAppGlobals* globals = state->dlgHdr.globals;
|
|
#endif
|
|
for ( ii = 0; ii < state->nItems; ++ii ) {
|
|
XP_ASSERT( ii == 0 || !!state->files[ii] );
|
|
if ( ii > 0 ) {
|
|
XP_FREE( globals->mpool, state->files[ii] );
|
|
}
|
|
XP_FREE( globals->mpool, state->names[ii] );
|
|
}
|
|
}
|
|
|
|
static XP_Bool
|
|
getSelText( DllSelState* state )
|
|
{
|
|
XP_Bool gotIt = XP_FALSE;
|
|
HWND hDlg = state->dlgHdr.hDlg;
|
|
CEAppGlobals* globals = state->dlgHdr.globals;
|
|
|
|
XP_S16 sel = SendDlgItemMessage( hDlg, state->dllListID,
|
|
GETCURSEL(globals), 0, 0 );
|
|
|
|
if ( sel >= 0 && sel != state->initialSel ) {
|
|
gotIt = XP_TRUE;
|
|
if ( sel > 0 ) {
|
|
wcscpy( state->wbuf, state->files[sel] );
|
|
}
|
|
}
|
|
return gotIt;
|
|
} /* getSelText */
|
|
|
|
LRESULT CALLBACK
|
|
DllSelDlg( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam )
|
|
{
|
|
DllSelState* state;
|
|
BOOL result = FALSE;
|
|
|
|
if ( message == WM_INITDIALOG ) {
|
|
SetWindowLongPtr( hDlg, GWL_USERDATA, lParam );
|
|
|
|
state = (DllSelState*)lParam;
|
|
state->cancelled = XP_TRUE;
|
|
|
|
state->dllListID = LB_IF_PPC( state->dlgHdr.globals, LOCALES_COMBO );
|
|
|
|
ceDlgSetup( &state->dlgHdr, hDlg, DLG_STATE_NONE );
|
|
|
|
ceDlgComboShowHide( &state->dlgHdr, LOCALES_COMBO );
|
|
|
|
result = TRUE;
|
|
} else {
|
|
state = (DllSelState*)GetWindowLongPtr( hDlg, GWL_USERDATA );
|
|
if ( !!state ) {
|
|
if ( !state->inited ) {
|
|
state->inited = XP_TRUE;
|
|
listDlls( state );
|
|
}
|
|
|
|
if ( ceDoDlgHandle( &state->dlgHdr, message, wParam, lParam) ) {
|
|
result = TRUE;
|
|
} else if ( (WM_COMMAND == message)
|
|
&& (BN_CLICKED == HIWORD(wParam)) ) {
|
|
switch( LOWORD(wParam) ) {
|
|
case IDOK:
|
|
state->cancelled = !getSelText( state );
|
|
/* fallthrough */
|
|
case IDCANCEL:
|
|
unlistDlls( state );
|
|
EndDialog( hDlg, LOWORD(wParam) );
|
|
result = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
} /* DllSelDlg */
|
|
|
|
typedef struct _FindOneData {
|
|
XP_UCHAR* buf;
|
|
XP_U16 bufLen;
|
|
XP_Bool called;
|
|
} FindOneData;
|
|
|
|
|
|
static XP_Bool
|
|
findFirstCB( const WIN32_FIND_DATA* data, const wchar_t* XP_UNUSED(name),
|
|
void* closure )
|
|
{
|
|
FindOneData* fd = (FindOneData*)closure;
|
|
|
|
(void)WideCharToMultiByte( CP_ACP, 0, data->cFileName, -1,
|
|
fd->buf, fd->bufLen, NULL, NULL );
|
|
|
|
fd->called = XP_TRUE;
|
|
return XP_FALSE; /* don't continue */
|
|
}
|
|
|
|
/* Pick the first .dll found, if any */
|
|
XP_Bool
|
|
ceGetOneResFile( XP_UCHAR* buf, XP_U16 bufLen )
|
|
{
|
|
FindOneData fod = {
|
|
.buf = buf,
|
|
.bufLen = bufLen,
|
|
.called = XP_FALSE,
|
|
};
|
|
for_each_dll( findFirstCB, &fod );
|
|
return fod.called;
|
|
}
|
|
|
|
/* ceChooseResFile: List all the available .rc files and return if user
|
|
* chooses one different from the one passed in.
|
|
*/
|
|
XP_Bool
|
|
ceChooseResFile( HWND hwnd, CEAppGlobals* globals, const XP_UCHAR* curFileName,
|
|
XP_UCHAR* buf, XP_U16 bufLen )
|
|
{
|
|
DllSelState state;
|
|
wchar_t wCurFile[MAX_PATH];
|
|
|
|
XP_MEMSET( &state, 0, sizeof(state) );
|
|
|
|
state.dlgHdr.globals = globals;
|
|
|
|
if ( !!curFileName ) {
|
|
(void)MultiByteToWideChar( CP_ACP, 0, curFileName, -1,
|
|
wCurFile, VSIZE(wCurFile) );
|
|
state.curFile = wCurFile;
|
|
}
|
|
|
|
(void)DialogBoxParam( globals->locInst, (LPCTSTR)IDD_LOCALESDLG,
|
|
hwnd, (DLGPROC)DllSelDlg, (long)&state );
|
|
|
|
if ( !state.cancelled ) {
|
|
(void)WideCharToMultiByte( CP_ACP, 0, state.wbuf, -1,
|
|
buf, bufLen, NULL, NULL );
|
|
}
|
|
|
|
LOG_RETURNF( "%s", buf );
|
|
return !state.cancelled;
|
|
} /* ceChooseResFile */
|