xwords/xwords4/palm/palmdict.c
ehouse 9e76871306 (conditionally) modify dicts to support getting height of platform
glyphs, pass into drawing code so can be fetched and cached, and
implement for Palm.  This is to allow drawing glyphs (like Danish's
A-with-circle) that are taller than normal without the visual padding
the rest can have.  So far works for 68K/non-highres.  Will not
compile for ARM yet.
2005-07-07 03:46:07 +00:00

564 lines
17 KiB
C

/* -*-mode: C; fill-column: 78; c-basic-offset: 4;-*- */
/*
* Copyright 1997-2005 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.
*/
#include <PalmTypes.h>
#include <DataMgr.h>
#include <VFSMgr.h>
#include <FeatureMgr.h>
#include "dictnryp.h"
#include "dawg.h"
#include "palmdict.h"
#include "dictlist.h"
#include "dictui.h"
#include "palmmain.h"
#include "pace_man.h" /* READ_UNALIGNED16 */
#include "LocalizedStrIncludes.h"
typedef struct DictStart {
unsigned long indexStart;
array_edge* array;
} DictStart;
#define NO_REFCOUNT -2
struct PalmDictionaryCtxt {
DictionaryCtxt super;
dawg_header* headerRecP;
DmOpenRef dbRef;
XP_U16 nRecords;
UInt16 cardNo; /* added to track VFS imported file for */
LocalID dbID; /* deletion later */
XP_UCHAR location; /* VFS or storage mem */
XP_S8 refCount;
PalmDictList* dl;
DictStart dictStarts[1];
};
#ifdef NODE_CAN_4
typedef unsigned short FaceType;
#else
typedef unsigned char FaceType;
#endif
static void palm_dictionary_destroy( DictionaryCtxt* dict );
static XP_U16 countSpecials( FaceType* ptr, UInt16 nChars );
static void setupSpecials( MPFORMAL PalmDictionaryCtxt* ctxt,
Xloc_specialEntry* specialStart, XP_U16 nSpecials );
static array_edge* palm_dict_edge_for_index_multi( DictionaryCtxt* dict,
XP_U32 index );
#ifdef TALL_FONTS
static void palm_dict_getFaceBounds( DictionaryCtxt* dict, Tile tile,
XP_FontBounds* bounds );
#endif
DictionaryCtxt*
palm_dictionary_make( MPFORMAL PalmAppGlobals* globals,
XP_UCHAR* dictName, PalmDictList* dl )
{
Boolean found;
UInt16 cardNo;
LocalID dbID;
DmOpenRef dbRef;
PalmDictionaryCtxt tDictBuf;
PalmDictionaryCtxt* ctxt;
MemHandle tmpH;
dawg_header* headerRecP;
unsigned char* charPtr;
UInt16 nChars, nSpecials;
XP_U32 offset;
DictListEntry* dle;
Err err;
XP_U16 i;
FaceType* facePtr;
#ifdef NODE_CAN_4
XP_U16 flags;
XP_U16 nodeSize = 3; /* init to satisfy compiler */
#endif
XP_U32 totalSize;
/* check and see if there's already a dict for this name. If yes,
increment its refcount and return. */
if ( !!dictName && getDictWithName( dl, dictName, &dle ) ) {
PalmDictionaryCtxt* dict = (PalmDictionaryCtxt*)dle->dict;
if ( !!dict ) {
++dict->refCount;
return (DictionaryCtxt*)dict;
}
}
if ( !!dictName ) {
ctxt = &tDictBuf;
} else {
/* If the name's NULL, we still need to create a dict, as the server
may be called to fill it in from the stream later. */
ctxt = XP_MALLOC( mpool, sizeof(*ctxt) );
}
XP_MEMSET( ctxt, 0, sizeof(*ctxt) );
MPASSIGN( ctxt->super.mpool, mpool );
dict_super_init( (DictionaryCtxt*)ctxt );
if ( !!dictName ) {
XP_ASSERT( XP_STRLEN((const char*)dictName) > 0 );
ctxt->super.name = dictName;
ctxt->super.destructor = palm_dictionary_destroy;
found = getDictWithName( dl, dictName, &dle );
if ( !found ) {
goto errExit;
}
if ( dle->location == DL_VFS ) {
XP_UCHAR* str = getResString( globals, STR_DICT_COPY_EXPL );
WinDrawChars( str, XP_STRLEN(str), 5, 40 );
err = VFSImportDatabaseFromFile( dle->u.vfsData.volNum,
(const char*)dle->path,
&cardNo, &dbID );
if ( err != errNone ) {
goto errExit;
}
} else {
cardNo = dle->u.dmData.cardNo;
dbID = dle->u.dmData.dbID;
}
ctxt->refCount = 1;
ctxt->dl = dl;
ctxt->dbID = dbID;
ctxt->cardNo = cardNo;
ctxt->location = dle->location;
ctxt->dbRef = dbRef = DmOpenDatabase( cardNo, dbID, dmModeReadOnly );
tmpH = DmQueryRecord( dbRef, 0 ); // <- <eeh> should be a constant
ctxt->headerRecP = headerRecP = (dawg_header*)MemHandleLock( tmpH );
XP_ASSERT( MemHandleLockCount(tmpH) == 1 );
#ifdef NODE_CAN_4
flags = XP_NTOHS( headerRecP->flags );
if ( flags == 0x0002 ) {
XP_ASSERT( nodeSize == 3 );
} else if ( flags == 0x0003 ) {
nodeSize = 4;
} else {
XP_WARNF( "got flags of %d", flags );
XP_ASSERT(0);
return NULL;
}
#endif
tmpH = DmQueryRecord( dbRef, headerRecP->charTableRecNum );
XP_ASSERT( !!tmpH );
facePtr = (FaceType*)MemHandleLock( tmpH );
XP_ASSERT( MemHandleLockCount( tmpH ) == 1 );
ctxt->super.nFaces = nChars = MemPtrSize(facePtr) / sizeof(*facePtr);
ctxt->super.faces16 =
XP_MALLOC( mpool, nChars * sizeof(ctxt->super.faces16[0]));
XP_ASSERT( !!ctxt->super.faces16 );
for ( i = 0; i < nChars; ++i ) {
#ifdef NODE_CAN_4
ctxt->super.faces16[i] = READ_UNALIGNED16( &facePtr[i] );
#else
ctxt->super.faces16[i] = facePtr[i];
#endif
}
nSpecials = countSpecials( facePtr, nChars );
MemPtrUnlock( facePtr );
tmpH = DmQueryRecord( dbRef, headerRecP->valTableRecNum );
charPtr = (unsigned char*)MemHandleLock(tmpH);
XP_ASSERT( MemHandleLockCount( tmpH ) == 1 );
// use 2; ARM thinks sizeof(Xloc_header) is 4.
#ifdef TALL_FONTS
ctxt->super.langCode = XP_NTOHS(*(XP_U16*)charPtr);
XP_LOGF( "read langCode of %x", ctxt->super.langCode );
#endif
ctxt->super.countsAndValues = charPtr + 2;
/* for those dicts with special chars */
if ( nSpecials > 0 ) {
Xloc_specialEntry* specialStart = (Xloc_specialEntry*)
(ctxt->super.countsAndValues + (ctxt->super.nFaces*2));
setupSpecials( MPPARM(mpool) ctxt, specialStart, nSpecials );
}
if ( headerRecP->firstEdgeRecNum == 0 ) { /* emtpy dict */
ctxt->super.topEdge = NULL;
ctxt->nRecords = 0;
} else {
MemHandle record;
XP_U16 size;
short index;
XP_U16 nRecords;
nRecords = DmNumRecords(dbRef) - headerRecP->firstEdgeRecNum;
/* alloacate now that we know the size. */
ctxt = XP_MALLOC( mpool,
sizeof(*ctxt) + (nRecords * sizeof(DictStart)));
XP_MEMCPY( ctxt, &tDictBuf, sizeof(tDictBuf) );
ctxt->nRecords = nRecords;
#ifdef NODE_CAN_4
ctxt->super.nodeSize = (XP_U8)nodeSize;
ctxt->super.is_4_byte = nodeSize == 4;
#endif
totalSize = 0;
for ( index = 0; index < nRecords; ++index ) {
record = DmQueryRecord( dbRef, index
+ headerRecP->firstEdgeRecNum);
totalSize += MemHandleSize( record );
}
/* NOTE: need to use more than one feature to support having
multiple dicts open at once. */
err = ~errNone; /* so test below will pass if nRecords == 1 */
#ifdef XWFEATURE_COMBINEDAWG
if ( nRecords > 1 ) {
void* dawgBase;
err = FtrPtrNew( APPID, DAWG_STORE_FEATURE, totalSize,
&dawgBase );
if ( err == errNone ) {
for ( index = 0, offset = 0; index < nRecords; ++index ) {
record = DmQueryRecord( dbRef, index
+ headerRecP->firstEdgeRecNum );
size = MemHandleSize( record );
XP_LOGF( "size=%d", size );
err = DmWrite( dawgBase, offset,
MemHandleLock( record ), size );
XP_ASSERT( err == errNone );
MemHandleUnlock( record );
offset += size;
XP_LOGF( "offset now = %ld", offset );
#ifdef DEBUG
#ifdef NODE_CAN_4
ctxt->super.numEdges += size / nodeSize;
#else
ctxt->super.numEdges += size / 3;
#endif
#endif
}
ctxt->super.base = dawgBase;
ctxt->super.topEdge = dawgBase;
} else {
XP_LOGF( "unable to use Ftr for dict; err=%d", err );
}
}
#endif
if ( err != errNone ) {
offset = 0;
for ( index = 0; index < nRecords; ++index ) {
record = DmQueryRecord( dbRef, index + headerRecP->firstEdgeRecNum );
size = MemHandleSize( record );
ctxt->dictStarts[index].indexStart = offset;
/* cast to short to avoid libc call */
XP_ASSERT( size < 0xFFFF );
#ifdef NODE_CAN_4
XP_ASSERT( 0 == (size % nodeSize) );
offset += size / nodeSize;
#else
XP_ASSERT( ((unsigned short)size % 3 )==0);
offset += ((unsigned short)size) / 3;
#endif
ctxt->dictStarts[index].array =
(array_edge*)MemHandleLock( record );
XP_ASSERT( MemHandleLockCount(record) == 1 );
}
XP_ASSERT( index == ctxt->nRecords );
ctxt->dictStarts[index].indexStart = 0xFFFFFFFFL;
ctxt->super.topEdge = ctxt->dictStarts[0].array;
#ifdef DEBUG
ctxt->super.numEdges = offset;
#endif
ctxt->super.func_edge_for_index
= palm_dict_edge_for_index_multi;
#ifdef TALL_FONTS
ctxt->super.func_dict_getFaceBounds
= palm_dict_getFaceBounds;
#endif
}
}
setBlankTile( (DictionaryCtxt*)ctxt );
cacheDictForName( dl, dictName, (DictionaryCtxt*)ctxt );
} else {
ctxt->refCount = NO_REFCOUNT;
}
return (DictionaryCtxt*)ctxt;
errExit:
if ( !!ctxt ) {
XP_ASSERT( ctxt == &tDictBuf );
}
return NULL;
} /* palm_dictionary_make */
static XP_U16
countSpecials( FaceType* ptr, UInt16 nChars )
{
XP_U16 result = 0;
while ( nChars-- ) {
FaceType face;
#ifdef NODE_CAN_4
XP_ASSERT( sizeof(face) == 2 );
face = READ_UNALIGNED16( ptr );
++ptr;
#else
face = *ptr++;
#endif
if ( IS_SPECIAL(face) ) {
++result;
}
}
XP_LOGF( "countSpecials=>%d", result );
return result;
} /* countSpecials */
/* We need an array of ptrs to bitmaps plus an array of ptrs to the text
* equivalents.
*/
static void
setupSpecials( MPFORMAL PalmDictionaryCtxt* ctxt,
Xloc_specialEntry* specialStart, XP_U16 nSpecials )
{
XP_U16 i;
char* base;
XP_UCHAR** chars;
SpecialBitmaps* bitmaps;
XP_LOGF( "setupSpecials" );
base = (char*)specialStart;
chars = XP_MALLOC( mpool, nSpecials * sizeof(*chars) );
bitmaps = XP_MALLOC( mpool, nSpecials * sizeof(*bitmaps) );
XP_MEMSET( bitmaps, 0, nSpecials * sizeof(*bitmaps ) );
for ( i = 0; i < nSpecials; ++i ) {
XP_U16 hasLarge, hasSmall;
chars[i] = specialStart->textVersion;
/* This may not work! Get rid of NTOHS???? */
hasLarge = READ_UNALIGNED16( &specialStart->hasLarge );
XP_LOGF( "hasLarge: %d", hasLarge );
if ( hasLarge ) {
bitmaps[i].largeBM = base + hasLarge;
}
hasSmall = READ_UNALIGNED16( &specialStart->hasSmall );
XP_LOGF( "hasSmall: %d", hasSmall );
if ( hasSmall ) {
bitmaps[i].smallBM = base + hasSmall;
}
++specialStart;
}
ctxt->super.bitmaps = bitmaps;
ctxt->super.chars = chars;
XP_LOGF( "setupSpecials done" );
} /* setupSpecials */
#ifdef TALL_FONTS
#define BMP_WIDTH 16
#define BMP_HT 16
static void
measureFace( XP_CHAR16 face, XP_U16* top, XP_U16* bottom )
{
WinHandle win;
BitmapType* bitmap;
Coord x, y;
Err err;
XP_Bool gotIt;
bitmap = BmpCreate( BMP_WIDTH, BMP_HT, 1, NULL, &err );
if ( err == errNone ) {
win = WinCreateBitmapWindow( bitmap, &err );
if ( err == errNone ) {
WinHandle oldWin = WinSetDrawWindow( win );
WinSetBackColor( 0 ); /* white */
(void)WinSetTextColor( 1 ); /* black */
(void)WinSetDrawMode( winOverlay );
WinDrawChar( face, 0, 0 );
/* Scan from top for top, then from bottom for botton */
gotIt = XP_FALSE;
for ( y = 0; !gotIt && y < BMP_HT; ++y ) {
for ( x = 0; !gotIt && x < BMP_WIDTH; ++x ) {
IndexedColorType pxl = WinGetPixel( x, y );
if ( pxl != 0 ) {
*top = y;
gotIt = XP_TRUE;
}
}
}
gotIt = XP_FALSE;
for ( y = BMP_HT - 1; !gotIt && y >= 0; --y ) {
for ( x = 0; !gotIt && x < BMP_WIDTH; ++x ) {
IndexedColorType pxl = WinGetPixel( x, y );
if ( pxl != 0 ) {
*bottom = y;
gotIt = XP_TRUE;
}
}
}
(void)WinSetDrawWindow( oldWin );
WinDeleteWindow( win, false );
}
BmpDelete( bitmap );
}
} /* measureFace */
static void
palm_dict_getFaceBounds( DictionaryCtxt* ctxt, Tile tile,
XP_FontBounds* bounds )
{
XP_CHAR16 face = ctxt->faces16[tile];
if ( !IS_SPECIAL(face) ) {
XP_U16 top, bottom;
measureFace( face, &top, &bottom );
bounds->topOffset = top;
bounds->height = bottom - top + 1;
XP_LOGF( "Char %c ranges from %d to %d",
(char)face, top, bottom );
}
} /* palm_dict_getFaceBounds */
#endif
static void
palm_dictionary_destroy( DictionaryCtxt* dict )
{
PalmDictionaryCtxt* ctxt = (PalmDictionaryCtxt*)dict;
if ( ctxt->refCount != NO_REFCOUNT && --ctxt->refCount == 0 ) {
short i;
DmOpenRef dbRef;
dawg_header* headerRecP;
if ( !!ctxt->super.name ) {
/* Remove from dict list */
removeFromDictCache( ctxt->dl, ctxt->super.name, dict );
}
dbRef = ctxt->dbRef;
headerRecP = ctxt->headerRecP;
XP_ASSERT( !!dbRef );
MemPtrUnlock( ctxt->super.countsAndValues - 2 );//sizeof(Xloc_header) );
XP_FREE( dict->mpool, ctxt->super.faces16 );
#ifdef XWFEATURE_COMBINEDAWG
/* Try first to delete the feature. */
if ( FtrPtrFree( APPID, DAWG_STORE_FEATURE ) == ftrErrNoSuchFeature ) {
#endif
for ( i = 0; i < ctxt->nRecords; ++i ) {
XP_ASSERT( !!ctxt->dictStarts[i].array );
MemPtrUnlock( ctxt->dictStarts[i].array );
}
#ifdef XWFEATURE_COMBINEDAWG
} else {
XP_ASSERT( ctxt->dictStarts[0].array == NULL );
}
#endif
MemPtrUnlock( headerRecP );
DmCloseDatabase( dbRef );
if ( !!ctxt->super.name ) {
XP_FREE( dict->mpool, ctxt->super.name );
}
if ( !!ctxt->super.bitmaps ) {
XP_FREE( dict->mpool, ctxt->super.bitmaps );
}
if ( !!ctxt->super.chars ) {
XP_FREE( dict->mpool, ctxt->super.chars );
}
/* if we copied the db to memory we should nuke it, since user would
have copied it himself if he wanted it here permanently */
if ( ctxt->location == DL_VFS ) {
DmDeleteDatabase( ctxt->cardNo, ctxt->dbID );
}
XP_FREE( dict->mpool, dict );
}
} /* palm_dictionary_destroy */
#ifdef OVERRIDE_EDGE_FOR_INDEX
static array_edge*
palm_dict_edge_for_index_multi( DictionaryCtxt* dict, XP_U32 index )
{
PalmDictionaryCtxt* ctxt = (PalmDictionaryCtxt*)dict;
array_edge* result;
XP_ASSERT( index < dict->numEdges );
if ( index == 0 ) {
result = NULL;
} else {
DictStart* headerP;
for ( headerP = &ctxt->dictStarts[1];
index >= headerP->indexStart;
++headerP ) {
/* do nothing */
}
--headerP;
index -= headerP->indexStart;
/* To avoid linking in __mulsi3, do the math without a variable */
if ( 0 ) {
#ifdef NODE_CAN_4
} else if ( dict->nodeSize == 4 ) {
index *= 4; /* better to shift (<<= 2) here? */
# ifdef DEBUG
} else if ( dict->nodeSize != 3 ) {
XP_ASSERT( 0 );
# endif
#endif
} else {
index *= 3;
}
result = headerP->array + index;
}
return result;
} /* dict_edge_for_index */
#endif