xwords/xwords4/wince/cedraw.c

1743 lines
52 KiB
C
Raw Normal View History

/* -*- fill-column: 77; compile-command: "make TARGET_OS=wince DEBUG=TRUE"; -*- */
2003-11-20 17:26:35 +01:00
/*
* Copyright 2000-2008 by Eric House (xwords@eehouse.org). All rights reserved.
2003-11-20 17:26:35 +01:00
*
* 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 <stdlib.h> */
/* #include <stdio.h> */
#include <windowsx.h>
2003-11-20 17:26:35 +01:00
#include <stdio.h> /* for sprintf, etc. */
#include "xptypes.h"
#include "cedraw.h"
2003-11-20 17:26:35 +01:00
#include "board.h"
#include "draw.h"
#include "mempool.h"
#include "cemain.h"
#include "cedict.h"
#include "cedefines.h"
2006-01-28 20:03:10 +01:00
#include "debhacks.h"
2003-11-20 17:26:35 +01:00
#ifndef DRAW_FUNC_NAME
#define DRAW_FUNC_NAME(nam) ce_draw_ ## nam
#endif
//#define MEASURE_OLD_WAY
#define CE_MINI_V_PADDING 6
#define CE_MINIW_PADDING 0
#define CE_SCORE_PADDING -3
#define CE_REM_PADDING -1
#define CE_TIMER_PADDING -2
#define IS_TURN_VPAD 2
#define DRAW_FOCUS_FRAME 1
#ifdef DRAW_FOCUS_FRAME
# define CE_FOCUS_BORDER_WIDTH 6
# define TREAT_AS_CURSOR(d,f) ((((f) & CELL_ISCURSOR) != 0) && !(d)->topFocus )
#else
# define TREAT_AS_CURSOR(d,f) (((f) & CELL_ISCURSOR) != 0)
#endif
typedef enum {
RFONTS_TRAY
,RFONTS_TRAYVAL
,RFONTS_CELL
,RFONTS_PTS
,N_RESIZE_FONTS
} RFIndex;
typedef struct _PenColorPair {
COLORREF ref;
HGDIOBJ pen;
} PenColorPair;
typedef struct _FontCacheEntry {
HFONT setFont;
XP_U16 setFontHt;
XP_U16 offset;
XP_U16 actualHt;
} FontCacheEntry;
struct CEDrawCtx {
DrawCtxVTable* vtable;
HWND mainWin;
CEAppGlobals* globals;
const DictionaryCtxt* dict;
COLORREF prevBkColor;
HBRUSH brushes[CE_NUM_COLORS];
PenColorPair pens[CE_NUM_COLORS];
HFONT selPlayerFont;
HFONT playerFont;
FontCacheEntry fcEntry[N_RESIZE_FONTS];
HBITMAP rightArrow;
HBITMAP downArrow;
HBITMAP origin;
XP_U16 trayOwner;
XP_U16 miniLineHt;
XP_Bool scoreIsVertical;
XP_Bool topFocus;
XP_Bool beenCleared;
MPSLOT
};
static void ceClearToBkground( CEDrawCtx* dctx, const XP_Rect* rect );
static void ceDrawBitmapInRect( HDC hdc, const RECT* r, HBITMAP bitmap );
static void ceClipToRect( HDC hdc, const RECT* rt );
static void ceClearFontCache( CEDrawCtx* dctx );
2003-11-20 17:26:35 +01:00
static void
XPRtoRECT( RECT* rt, const XP_Rect* xprect )
2003-11-20 17:26:35 +01:00
{
rt->left = xprect->left;
rt->top = xprect->top;
rt->right = rt->left + xprect->width;
rt->bottom = rt->top + xprect->height;
} /* XPRtoRECT */
static HGDIOBJ
ceGetPen( CEDrawCtx* dctx, XP_U16 colorIndx, XP_U16 width )
{
PenColorPair* pair = &dctx->pens[colorIndx];
HGDIOBJ pen = pair->pen;
COLORREF ref = dctx->globals->appPrefs.colors[colorIndx];
/* Make sure cached value is still good */
if ( !!pen && (ref != pair->ref) ) {
DeleteObject( pen );
pen = NULL;
}
if ( !pen ) {
pen = CreatePen( PS_SOLID, width, ref );
pair->pen = pen;
pair->ref = ref;
}
return pen;
}
2003-11-20 17:26:35 +01:00
static void
makeAndDrawBitmap( CEDrawCtx* dctx, HDC hdc, const RECT* bnds, XP_Bool center,
XP_U16 colorIndx, CEBitmapInfo* info )
2003-11-20 17:26:35 +01:00
{
#if 1
HGDIOBJ forePen = ceGetPen( dctx, colorIndx, 1 );
2003-11-20 17:26:35 +01:00
POINT points[2];
XP_U16 nCols, nRows, row, col, rowBytes;
XP_S32 x, y;
2008-02-16 18:19:31 +01:00
XP_U8* bits = info->bits;
2003-11-20 17:26:35 +01:00
HGDIOBJ oldObj;
oldObj = SelectObject( hdc, forePen );
nRows = info->nRows;
nCols = info->nCols;
rowBytes = (nCols + 7) / 8;
while ( (rowBytes % 2) != 0 ) {
++rowBytes;
}
x = bnds->left;
y = bnds->top;
if ( center ) {
/* the + 1 is to round up */
x += ((bnds->right - bnds->left) - nCols + 1) / 2;
y += ((bnds->bottom - bnds->top) - nRows + 1) / 2;
}
2003-11-20 17:26:35 +01:00
for ( row = 0; row < nRows; ++row ) {
for ( col = 0; col < nCols; ++col ) {
2008-02-16 18:19:31 +01:00
XP_U8 byt = bits[col / 8];
2003-11-20 17:26:35 +01:00
XP_Bool set = (byt & (0x80 >> (col % 8))) != 0;
if ( set ) {
points[0].x = x + col;
points[0].y = y + row;
points[1].x = x + col + 1;
points[1].y = y + row + 1;
Polyline( hdc, points, 2 );
}
}
bits += rowBytes;
}
SelectObject( hdc, oldObj );
#else
/* I can't get this to work. Hence the above hack.... */
2003-11-20 17:26:35 +01:00
HBITMAP bm;
bm = CreateBitmap( info->nCols, info->nRows, 1, 1,
info->bits );
ceDrawBitmapInRect( hdc, rt->left+2, rt->top+2, bm );
DeleteObject( bm );
#endif
} /* makeAndDrawBitmap */
static void
ceDrawTextClipped( HDC hdc, wchar_t* buf, XP_S16 len, XP_Bool clip,
XP_U16 left, XP_U16 top, XP_U16 width, XP_U16 height,
XP_U16 offset, XP_U16 hJust )
{
RECT rect;
rect.left = left;
rect.top = top;
rect.bottom = top + height;
rect.right = left + width;
if ( clip ) {
ceClipToRect( hdc, &rect );
}
rect.top -= offset;
/* XP_LOGF( "%s: drawing left: %ld, top: %ld, right: %ld, bottom: %ld", */
/* __func__, rect.left, rect.top, rect.right, rect.bottom ); */
DrawText( hdc, buf, len, &rect, DT_SINGLELINE | DT_TOP | hJust );
} /* ceDrawTextClipped */
/* CE doesn't have FrameRect, so we'll roll our own */
static int
ceDrawFocusRect( HDC hdc, CEDrawCtx* dctx, LPCRECT rect )
{
RECT lr = *rect;
HGDIOBJ oldObj;
HGDIOBJ pen = ceGetPen( dctx, CE_FOCUS_COLOR, CE_FOCUS_BORDER_WIDTH );
InsetRect( &lr, CE_FOCUS_BORDER_WIDTH/2, CE_FOCUS_BORDER_WIDTH/2 );
oldObj = SelectObject( hdc, pen );
MoveToEx( hdc, lr.left, lr.top, NULL );
LineTo( hdc, lr.right, lr.top );
LineTo( hdc, lr.right, lr.bottom );
LineTo( hdc, lr.left, lr.bottom );
LineTo( hdc, lr.left, lr.top );
SelectObject( hdc, oldObj );
return 0;
}
static void
ceClipToRect( /* CEDrawCtx* dctx, */HDC hdc, const RECT* rt )
{
/*
NULLREGION Region is empty.
SIMPLEREGION Region is a single rectangle.
COMPLEXREGION Region is more than one rectangle.
ERROR
*/
#if 0
/* Docs suggest I should be able to reuse the region but it doesn't
work. At least under WINE... */
HRGN clipRgn = dctx->clipRgn;
int ret;
if ( !clipRgn ) {
clipRgn = CreateRectRgn( rt->left, rt->top, rt->right, rt->bottom );
dctx->clipRgn = clipRgn;
} else {
(void)SetRectRgn( clipRgn, rt->left, rt->top, rt->right, rt->bottom );
}
(void)SelectClipRgn( hdc, clipRgn ); /* returns SIMPLEREGION */
#else
HRGN clipRgn = CreateRectRgn( rt->left, rt->top, rt->right, rt->bottom );
SelectClipRgn( hdc, clipRgn );
DeleteObject( clipRgn );
#endif
} /* ceClipToRect */
static void
makeTestBuf( CEDrawCtx* dctx, XP_UCHAR* buf, XP_UCHAR bufLen, RFIndex index )
{
switch( index ) {
case RFONTS_TRAY:
case RFONTS_CELL: {
Tile tile;
Tile blank = (Tile)-1;
const DictionaryCtxt* dict = dctx->dict;
XP_U16 nFaces = dict_numTileFaces( dict );
Tile tiles[nFaces];
XP_U16 nOut = 0;
XP_ASSERT( !!dict && nFaces < bufLen );
if ( dict_hasBlankTile(dict) ) {
blank = dict_getBlankTile( dict );
}
for ( tile = 0; tile < nFaces; ++tile ) {
if ( tile != blank ) {
tiles[nOut++] = tile;
}
}
(void)dict_tilesToString( dict, tiles, nOut, buf, bufLen );
}
break;
case RFONTS_TRAYVAL:
strcpy( buf, "0" ); /* all numbers the same :-) */
break;
case RFONTS_PTS:
strcpy( buf, "Pts:0?" );
break;
case N_RESIZE_FONTS:
XP_ASSERT(0);
}
XP_LOGF( "%s=>%s", __func__, buf );
} /* makeTestBuf */
#ifndef MEASURE_OLD_WAY
static void
ceMeasureGlyph( HDC hdc, HBRUSH white, wchar_t glyph,
XP_U16 minTopSeen, XP_U16 maxBottomSeen,
XP_U16* top, XP_U16* bottom )
{
SIZE size;
XP_U16 xx, yy;
XP_Bool done;
GetTextExtentPoint32( hdc, &glyph, 1, &size );
RECT rect = { 0, 0, size.cx, size.cy };
FillRect( hdc, &rect, white );
DrawText( hdc, &glyph, 1, &rect, DT_TOP | DT_LEFT );
/* char tbuf[size.cx+1]; */
/* for ( yy = 0; yy < size.cy; ++yy ) { */
/* XP_MEMSET( tbuf, 0, size.cx+1 ); */
/* for ( xx = 0; xx < size.cx; ++xx ) { */
/* COLORREF ref = GetPixel( hdc, xx, yy ); */
/* XP_ASSERT( ref != CLR_INVALID ); */
/* strcat( tbuf, ref==0? " " : "x" ); */
/* } */
/* XP_LOGF( "line[%.2d] = %s", yy, tbuf ); */
/* } */
/* Find out if this guy's taller than what we have */
for ( done = XP_FALSE, yy = 0; yy < minTopSeen && !done; ++yy ) {
for ( xx = 0; xx < size.cx; ++xx ) {
COLORREF ref = GetPixel( hdc, xx, yy );
if ( ref == CLR_INVALID ) {
break; /* done this line */
} else if ( ref == 0 ) { /* a pixel set! */
*top = yy;
done = XP_TRUE;
break;
}
}
}
/* Extends lower than seen */
for ( done = XP_FALSE, yy = size.cy - 1; yy > maxBottomSeen && !done; --yy ) {
for ( xx = 0; xx < size.cx; ++xx ) {
COLORREF ref = GetPixel( hdc, xx, yy );
if ( ref == CLR_INVALID ) {
break;
} else if ( ref == 0 ) { /* a pixel set! */
*bottom = yy;
done = XP_TRUE;
break;
}
}
}
/* XP_LOGF( "%s: top: %d; bottom: %d", __func__, *top, *bottom ); */
} /* ceMeasureGlyph */
static void
ceMeasureGlyphs( CEDrawCtx* dctx, HDC hdc, /* HFONT font, */wchar_t* str,
XP_U16* hasMinTop, XP_U16* hasMaxBottom )
{
HBRUSH white = dctx->brushes[CE_WHITE_COLOR];
XP_U16 ii;
XP_U16 len = wcslen(str);
XP_U16 minTopSeen, maxBottomSeen;
XP_U16 maxBottomIndex = 0;
XP_U16 minTopIndex = 0;
minTopSeen = 1000; /* really large... */
maxBottomSeen = 0;
for ( ii = 0; ii < len; ++ii ) {
XP_U16 thisTop, thisBottom;
ceMeasureGlyph( hdc, white, str[ii],
minTopSeen, maxBottomSeen,
&thisTop, &thisBottom );
if ( thisBottom > maxBottomSeen ) {
maxBottomSeen = thisBottom;
maxBottomIndex = ii;
}
if ( thisTop < minTopSeen ) {
minTopSeen = thisTop;
minTopIndex = ii;
}
}
/* XP_LOGF( "offset: %d; height: %d", minTopSeen, maxBottomSeen - minTopSeen + 1 ); */
/* XP_LOGF( "offset came from %d; height from %d", minTopIndex, maxBottomIndex ); */
*hasMinTop = minTopIndex;
*hasMaxBottom = maxBottomIndex;
} /* ceMeasureGlyphs */
#endif
static void
ceClearFontCache( CEDrawCtx* dctx )
{
XP_U16 ii;
for ( ii = 0; ii < N_RESIZE_FONTS; ++ii ) {
if ( !!dctx->fcEntry[ii].setFont ) {
DeleteObject( dctx->fcEntry[ii].setFont );
}
}
XP_MEMSET( &dctx->fcEntry, 0, sizeof(dctx->fcEntry) );
}
#ifdef MEASURE_OLD_WAY
static void
ceMeasureTextHt( HFONT font, const char* str, XP_U16* ht, XP_U16* offset )
{
XP_U16 len = strlen(str);
wchar_t widebuf[len+1];
HDC memDC;
HBITMAP memBM;
int xx, yy;
SIZE size;
int firstLine = 0, lastLine = -1;
memDC = CreateCompatibleDC( NULL );
MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, str, len,
widebuf, VSIZE(widebuf) );
SelectObject( memDC, font );
GetTextExtentPoint32( memDC, widebuf, len, &size );
memBM = CreateCompatibleBitmap( memDC, size.cx, size.cy );
SelectObject( memDC, memBM );
RECT r = { 0, 0, size.cx, size.cy };
DrawText( memDC, widebuf, len, &r, DT_TOP | DT_LEFT );
/* int GetDIBits( */
/* HDC hdc, // handle to DC */
/* HBITMAP hbmp, // handle to bitmap */
/* UINT uStartScan, // first scan line to set */
/* UINT cScanLines, // number of scan lines to copy */
/* LPVOID lpvBits, // array for bitmap bits */
/* LPBITMAPINFO lpbi, // bitmap data buffer */
/* UINT uUsage // RGB or palette index */
/* ); */
/* for ( yy = 0; yy < size.cy; ++yy ) { */
/* char tbuf[121] = { '\0' }; */
/* for ( xx = 0; xx < size.cx; ++xx ) { */
/* COLORREF ref = GetPixel( memDC, xx, yy ); */
/* if ( ref != CLR_INVALID ) { */
/* strcat( tbuf, ref==0? " " : "x" ); */
/* } */
/* } */
/* XP_LOGF( "line[%.2d] = %s", yy, tbuf ); */
/* } */
/* Try replacing GetPixel with something that loads in scan lines.
Should be faster... */
/* Get offset first: iterate down from top */
for ( yy = 0; yy < size.cy && firstLine == 0; ++yy ) {
for ( xx = 0; xx < size.cx; ++xx ) {
COLORREF ref = GetPixel( memDC, xx, yy );
if ( ref == CLR_INVALID ) {
break;
} else if ( ref == 0 ) { /* a pixel set! */
firstLine = yy;
break;
}
}
}
/* Get height: iterate up from bottom */
for ( yy = size.cy - 1; yy >= 0 && lastLine < 0; --yy ) {
for ( xx = 0; xx < size.cx; ++xx ) {
COLORREF ref = GetPixel( memDC, xx, yy );
if ( ref == CLR_INVALID ) {
break;
} else if ( ref == 0 ) { /* a pixel set! */
lastLine = yy;
break;
}
}
}
DeleteObject( memBM );
DeleteDC( memDC );
*offset = firstLine;
*ht = lastLine - firstLine + 1;
/* XP_LOGF( "%s(%s)=>ht: %d; offset: %d", __func__, str, *ht, *offset ); */
} /* ceMeasureTextHt */
#endif
static void
ceBestFitFont( CEDrawCtx* dctx, XP_U16 soughtHeight, RFIndex index,
FontCacheEntry* fce )
{
#ifdef MEASURE_OLD_WAY
HFONT font;
LOGFONT fontInfo;
char buf[65];
XP_U16 offset;
XP_U16 actualHt;
XP_U16 trialHt;
/* XP_LOGF( "%s: calculating for index: %d; height: %d", */
/* __func__, index, height ); */
makeTestBuf( dctx, buf, VSIZE(buf), index );
for ( trialHt = soughtHeight; ; /*++trialHt*/ ) {
XP_MEMSET( &fontInfo, 0, sizeof(fontInfo) );
fontInfo.lfHeight = trialHt;
HFONT testFont = CreateFontIndirect( &fontInfo );
XP_U16 testOffset, testHt;
/* XP_LOGF( "%s: looking for ht %d with testht %d", __func__, height, trialHt ); */
ceMeasureTextHt( testFont, buf, &testHt, &testOffset );
if ( testHt > soughtHeight ) {
/* we've gone too far, so choose last that fit!!! */
XP_ASSERT( !!font );
DeleteObject( testFont );
break;
/* } else if ( trialHt == height /\* first time through *\/ */
/* && testOffset > 0 ) { /\* for safety *\/ */
/* trialHt += testOffset; */
} else {
++trialHt;
}
DeleteObject( font );
font = testFont;
offset = testOffset;
actualHt = testHt;
}
if ( !!fce->setFont ) {
DeleteObject( fce->setFont );
}
fce->setFont = font;
fce->setFontHt = soughtHeight;
fce->offset = offset;
fce->actualHt = actualHt;
#else
wchar_t widebuf[65];
XP_U16 len;
XP_U16 hasMinTop, hasMaxBottom;
XP_Bool firstPass;
HBRUSH white = dctx->brushes[CE_WHITE_COLOR];
HDC memDC = CreateCompatibleDC( NULL );
HBITMAP memBM;
XP_U16 testSize;
XP_LOGF( "%s(index=%d)", __func__, index );
char sample[65];
makeTestBuf( dctx, sample, VSIZE(sample), index );
len = strlen(sample);
MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, sample, len,
widebuf, len );
memBM = CreateCompatibleBitmap( memDC, 64, 64 );
SelectObject( memDC, memBM );
for ( firstPass = XP_TRUE, testSize = soughtHeight*2; ; --testSize ) {
LOGFONT fontInfo;
XP_MEMSET( &fontInfo, 0, sizeof(fontInfo) );
fontInfo.lfHeight = testSize;
HFONT testFont = CreateFontIndirect( &fontInfo );
if ( !!testFont ) {
XP_U16 thisHeight, top, bottom;
SelectObject( memDC, testFont );
/* first time, measure all of them to determine which chars have
high and low points */
if ( firstPass ) {
ceMeasureGlyphs( dctx, memDC, widebuf, &hasMinTop,
&hasMaxBottom );
firstPass = XP_FALSE;
}
/* Thereafter, just measure the two we know about */
ceMeasureGlyph( memDC, white, sample[hasMinTop], 1000, 0,
&top, &bottom );
ceMeasureGlyph( memDC, white, sample[hasMaxBottom],
top, bottom, &top, &bottom );
thisHeight = bottom - top + 1;
if ( thisHeight <= soughtHeight ) { /* got it!!! */
fce->setFont = testFont;
fce->setFontHt = soughtHeight;
fce->offset = top;
fce->actualHt = thisHeight;
XP_LOGF( "Looking for %d; PICKED %d",
soughtHeight, thisHeight );
break;
}
DeleteObject( testFont );
XP_LOGF( "Looking for %d; rejected %d", soughtHeight, thisHeight );
}
}
DeleteObject( memBM );
DeleteDC( memDC );
#endif
} /* ceBestFitFont */
static HFONT
ceGetSizedFont( CEDrawCtx* dctx, XP_U16 height, RFIndex index,
XP_U16* offsetP, XP_U16* actualHtP )
{
FontCacheEntry* fce = &dctx->fcEntry[index];
if ( (0 != height) /* 0 means use what we have */
&& fce->setFontHt != height ) {
ceBestFitFont( dctx, height, index, fce );
}
if ( !!offsetP ) {
*offsetP = fce->offset;
}
if ( !!actualHtP ) {
*actualHtP = fce->actualHt;
}
XP_ASSERT( !!fce->setFont ); /* failing... */
return fce->setFont;
} /* ceGetSizedFont */
#if 0
/* I'm trying to measure individual chars, but GetGlyphOutline and
GetTextExtentExPointW both return the full font height for any char, even
'.'. GetGlyphOutline fails altogether on XP. Work in progress...
*/
static void
logAllChars( HDC hdc, wchar_t* widebuf, XP_U16 len )
{
wchar_t tmp[2] = { 0, 0 };
int i;
for ( i = 0; i < len; ++i ) {
GLYPHMETRICS gm;
DWORD dw;
XP_MEMSET( &gm, 0, sizeof(gm) );
dw = GetGlyphOutline( hdc,
widebuf[i], GGO_METRICS,
&gm,
0, // size of data buffer
NULL, // data buffer
NULL );
/* GetTextExtentPoint32( hdc, &widebuf[i], 1, &size ); */
tmp[0] = widebuf[i];
XP_LOGW( "letter: ", tmp );
XP_LOGF( "width: %d; height: %d (pt: %ld,%ld)", gm.gmBlackBoxX,
gm.gmBlackBoxY, gm.gmptGlyphOrigin.x, gm.gmptGlyphOrigin.y );
}
}
#endif
static void
2008-03-31 01:58:04 +02:00
ceMeasureText( CEDrawCtx* dctx, const XP_UCHAR* str, XP_S16 padding,
XP_U16* widthP, XP_U16* heightP )
{
HDC hdc = GetDC(dctx->mainWin);//globals->hdc;
XP_U16 height = 0;
XP_U16 maxWidth = 0;
for ( ; ; ) {
wchar_t widebuf[32];
XP_UCHAR* nextStr = strstr( str, XP_CR );
XP_U16 len = nextStr==NULL? strlen(str): nextStr - str;
SIZE size;
XP_ASSERT( nextStr != str );
MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, str, len,
widebuf, VSIZE(widebuf) );
GetTextExtentPoint32( hdc, widebuf, len, &size );
maxWidth = (XP_U16)XP_MAX( maxWidth, size.cx );
2006-05-11 04:58:36 +02:00
height += size.cy;
dctx->miniLineHt = (XP_U16)size.cy;
if ( nextStr == NULL ) {
break;
}
height += padding;
str = nextStr + XP_STRLEN(XP_CR); /* skip '\n' */
}
*widthP = maxWidth + 8;
*heightP = height;
2008-03-31 01:58:04 +02:00
} /* ceMeasureText */
static void
drawTextLines( CEDrawCtx* dctx, HDC hdc, const XP_UCHAR* text, XP_S16 padding,
const RECT* rp, int flags )
{
wchar_t widebuf[128];
RECT textRt = *rp;
for ( ; ; ) { /* draw up to the '\n' each time */
XP_UCHAR* nextStr = strstr( text, XP_CR );
XP_U16 len;
if ( nextStr == NULL ) {
len = XP_STRLEN(text);
} else {
len = nextStr - text;
}
MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, text, len,
widebuf, VSIZE(widebuf) );
textRt.bottom = textRt.top + dctx->miniLineHt;
DrawText( hdc, widebuf, len, &textRt, flags );
if ( nextStr == NULL ) {
break;
}
textRt.top = textRt.bottom + padding;
text = nextStr + XP_STRLEN(XP_CR);
}
} /* drawTextLines */
DLSTATIC XP_Bool
DRAW_FUNC_NAME(boardBegin)( DrawCtx* p_dctx,
const XP_Rect* XP_UNUSED(rect),
DrawFocusState dfs )
2003-11-20 17:26:35 +01:00
{
CEDrawCtx* dctx = (CEDrawCtx*)p_dctx;
CEAppGlobals* globals = dctx->globals;
HDC hdc = globals->hdc;
XP_Bool canDraw = !!hdc && !!dctx->dict;
if ( canDraw ) {
dctx->prevBkColor = GetBkColor( hdc );
dctx->topFocus = dfs == DFS_TOP;
}
return canDraw;
2003-11-20 17:26:35 +01:00
} /* draw_boardBegin */
DLSTATIC void
DRAW_FUNC_NAME(objFinished)( DrawCtx* p_dctx, BoardObjectType typ,
const XP_Rect* rect, DrawFocusState dfs )
2003-11-20 17:26:35 +01:00
{
#ifdef DRAW_FOCUS_FRAME
if ( (dfs == DFS_TOP) && (typ == OBJ_BOARD || typ == OBJ_TRAY) ) {
CEDrawCtx* dctx = (CEDrawCtx*)p_dctx;
CEAppGlobals* globals = dctx->globals;
HDC hdc = globals->hdc;
RECT rt;
XPRtoRECT( &rt, rect );
ceClipToRect( hdc, &rt );
ceDrawFocusRect( hdc, dctx, &rt );
}
#endif
2006-11-12 16:22:26 +01:00
}
2003-11-20 17:26:35 +01:00
static XP_U16
getPlayerColor( XP_S16 player )
{
if ( player < 0 ) {
return CE_BLACK_COLOR;
2003-11-20 17:26:35 +01:00
} else {
return CE_USER_COLOR1 + player;
2003-11-20 17:26:35 +01:00
}
} /* getPlayerColor */
2004-06-16 06:17:08 +02:00
static void
2004-12-12 18:39:59 +01:00
ceDrawLine( HDC hdc, XP_S32 x1, XP_S32 y1, XP_S32 x2, XP_S32 y2 )
2004-06-16 06:17:08 +02:00
{
MoveToEx( hdc, x1, y1, NULL );
LineTo( hdc, x2, y2);
2004-12-12 18:39:59 +01:00
} /* ceDrawLine */
static void
ceDrawHintBorders( HDC hdc, const XP_Rect* xprect, HintAtts hintAtts )
2004-12-12 18:39:59 +01:00
{
if ( hintAtts != HINT_BORDER_NONE && hintAtts != HINT_BORDER_CENTER ) {
RECT rt;
XPRtoRECT( &rt, xprect );
InsetRect( &rt, 1, 1 );
if ( (hintAtts & HINT_BORDER_LEFT) != 0 ) {
ceDrawLine( hdc, rt.left, rt.top, rt.left, rt.bottom+1 );
}
if ( (hintAtts & HINT_BORDER_RIGHT) != 0 ) {
ceDrawLine( hdc, rt.right, rt.top, rt.right, rt.bottom+1 );
}
if ( (hintAtts & HINT_BORDER_TOP) != 0 ) {
ceDrawLine( hdc, rt.left, rt.top, rt.right+1, rt.top );
}
if ( (hintAtts & HINT_BORDER_BOTTOM) != 0 ) {
ceDrawLine( hdc, rt.left, rt.bottom, rt.right+1, rt.bottom );
}
}
2004-06-16 06:17:08 +02:00
} /* ceDrawHintBorders */
DLSTATIC XP_Bool
DRAW_FUNC_NAME(drawCell)( DrawCtx* p_dctx, const XP_Rect* xprect,
2006-11-12 16:22:26 +01:00
const XP_UCHAR* letters, const XP_Bitmap bitmap,
Tile XP_UNUSED(tile), XP_S16 owner,
XWBonusType bonus, HintAtts hintAtts,
CellFlags flags )
2003-11-20 17:26:35 +01:00
{
CEDrawCtx* dctx = (CEDrawCtx*)p_dctx;
CEAppGlobals* globals = dctx->globals;
HDC hdc = globals->hdc;
RECT rt;
2004-06-16 06:17:08 +02:00
RECT textRect;
2003-11-20 17:26:35 +01:00
XP_U16 bkIndex;
XP_U16 foreColorIndx;
2006-11-12 16:22:26 +01:00
XP_Bool isPending = (flags & CELL_HIGHLIGHT) != 0;
XP_Bool isFocussed = TREAT_AS_CURSOR( dctx, flags );
XP_Bool isDragSrc = (flags & CELL_DRAGSRC) != 0;
HFONT oldFont;
XP_U16 offset, actualHt;
2003-11-20 17:26:35 +01:00
XP_ASSERT( !!hdc );
2003-11-20 17:26:35 +01:00
XPRtoRECT( &rt, xprect );
++rt.bottom;
++rt.right;
ceClipToRect( hdc, &rt );
2003-11-20 17:26:35 +01:00
Rectangle( hdc, rt.left, rt.top, rt.right, rt.bottom );
2004-06-16 06:17:08 +02:00
textRect = rt;
2003-11-20 17:26:35 +01:00
InsetRect( &rt, 1, 1 );
ceClipToRect( hdc, &rt );
2003-11-20 17:26:35 +01:00
oldFont = SelectObject( hdc, ceGetSizedFont( dctx, xprect->height - 3,
RFONTS_CELL, &offset,
&actualHt ) );
2006-05-28 18:16:46 +02:00
/* always init to silence compiler warning */
foreColorIndx = getPlayerColor(owner);
2006-05-28 18:16:46 +02:00
if ( !isDragSrc && ((!!letters && letters[0] != '\0' ) || !!bitmap )) {
if ( isPending ) {
bkIndex = CE_BLACK_COLOR;
foreColorIndx = CE_WHITE_COLOR;
2003-11-20 17:26:35 +01:00
} else {
bkIndex = CE_TILEBACK_COLOR;
// foreColorIndx already has right val
2003-11-20 17:26:35 +01:00
}
} else if ( bonus == BONUS_NONE ) {
bkIndex = CE_BKG_COLOR;
} else {
bkIndex = (bonus - BONUS_DOUBLE_LETTER) + CE_BONUS1_COLOR;
2003-11-20 17:26:35 +01:00
}
if ( isFocussed ) {
bkIndex = CE_FOCUS_COLOR;
}
2003-11-20 17:26:35 +01:00
FillRect( hdc, &rt, dctx->brushes[bkIndex] );
2006-11-12 16:22:26 +01:00
if ( (flags&CELL_ISBLANK) != 0 ) {
/* For some reason windoze won't let me paint just the corner pixels
when certain colors are involved, but it will let me paint the
whole rect and then erase all but the corners. File this under
"don't ask, but it works". */
RECT tmpRT;
FillRect( hdc, &rt,
dctx->brushes[isPending?CE_WHITE_COLOR:CE_BLACK_COLOR] );
tmpRT = rt;
InsetRect( &tmpRT, 0, 2 );
FillRect( hdc, &tmpRT, dctx->brushes[bkIndex] );
tmpRT = rt;
InsetRect( &tmpRT, 1, 0 );
FillRect( hdc, &tmpRT, dctx->brushes[bkIndex] );
}
2004-03-28 03:14:34 +02:00
SetBkColor( hdc, dctx->globals->appPrefs.colors[bkIndex] );
2003-11-20 17:26:35 +01:00
if ( !isDragSrc && !!letters && (letters[0] != '\0') ) {
2003-11-20 17:26:35 +01:00
wchar_t widebuf[4];
MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, letters, -1,
widebuf, VSIZE(widebuf) );
2003-11-20 17:26:35 +01:00
SetTextColor( hdc, dctx->globals->appPrefs.colors[foreColorIndx] );
#ifdef TARGET_OS_WIN32
MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, letters, -1,
widebuf, VSIZE(widebuf) );
#endif
ceDrawTextClipped( hdc, widebuf, -1, XP_FALSE, xprect->left+1,
xprect->top+2, xprect->width, actualHt, offset,
DT_CENTER );
} else if ( !isDragSrc && !!bitmap ) {
makeAndDrawBitmap( dctx, hdc, &rt, XP_TRUE,
foreColorIndx, (CEBitmapInfo*)bitmap );
2006-11-12 16:22:26 +01:00
} else if ( (flags&CELL_ISSTAR) != 0 ) {
InsetRect( &textRect, 1, 1 );
ceDrawBitmapInRect( hdc, &textRect, dctx->origin );
2003-11-20 17:26:35 +01:00
}
2004-12-12 18:39:59 +01:00
ceDrawHintBorders( hdc, xprect, hintAtts );
SelectObject( hdc, oldFont );
2003-11-20 17:26:35 +01:00
return XP_TRUE;
} /* ce_draw_drawCell */
DLSTATIC void
DRAW_FUNC_NAME(invertCell)( DrawCtx* XP_UNUSED(p_dctx),
const XP_Rect* XP_UNUSED(rect) )
2003-11-20 17:26:35 +01:00
{
} /* ce_draw_invertCell */
#ifdef DEBUG
#if 0
static char*
logClipResult( int icrResult )
{
#define caseStr(d) case d: return #d
switch ( icrResult ) {
caseStr(SIMPLEREGION);
caseStr(COMPLEXREGION);
caseStr(NULLREGION);
caseStr(ERROR);
}
#undef caseStr
return "unknown";
} /* logClipResult */
#endif
#endif
DLSTATIC XP_Bool
DRAW_FUNC_NAME(trayBegin)( DrawCtx* p_dctx, const XP_Rect* XP_UNUSED(rect),
XP_U16 owner, DrawFocusState dfs )
2003-11-20 17:26:35 +01:00
{
CEDrawCtx* dctx = (CEDrawCtx*)p_dctx;
2004-03-02 04:26:10 +01:00
CEAppGlobals* globals = dctx->globals;
HDC hdc = globals->hdc;
XP_Bool canDraw = !!hdc && !!dctx->dict;
2004-03-02 04:26:10 +01:00
if ( canDraw ) {
dctx->trayOwner = owner;
dctx->topFocus = dfs == DFS_TOP;
2004-03-02 04:26:10 +01:00
}
return canDraw;
2003-11-20 17:26:35 +01:00
} /* ce_draw_trayBegin */
static void
drawDrawTileGuts( DrawCtx* p_dctx, const XP_Rect* xprect,
const XP_UCHAR* letters,
2006-11-12 16:22:26 +01:00
XP_Bitmap bitmap, XP_S16 val, CellFlags flags )
2003-11-20 17:26:35 +01:00
{
CEDrawCtx* dctx = (CEDrawCtx*)p_dctx;
CEAppGlobals* globals = dctx->globals;
HDC hdc = globals->hdc;
wchar_t widebuf[4];
RECT rt;
2006-03-20 05:13:40 +01:00
XP_U16 index;
XP_Bool highlighted = XP_FALSE;
XP_Bool isFocussed = TREAT_AS_CURSOR(dctx,flags);
XP_Bool isEmpty = (flags & CELL_ISEMPTY) != 0;
2003-11-20 17:26:35 +01:00
XPRtoRECT( &rt, xprect );
ceClipToRect( hdc, &rt );
2003-11-20 17:26:35 +01:00
ceClearToBkground( dctx, xprect );
if ( !isEmpty || isFocussed ) {
XP_U16 backIndex = isFocussed? CE_FOCUS_COLOR : CE_TILEBACK_COLOR;
2004-01-10 19:02:10 +01:00
SetBkColor( hdc, dctx->globals->appPrefs.colors[backIndex] );
2003-11-20 17:26:35 +01:00
2004-01-10 19:02:10 +01:00
InsetRect( &rt, 1, 1 );
Rectangle(hdc, rt.left, rt.top, rt.right, rt.bottom); /* draw frame */
2004-01-10 19:02:10 +01:00
InsetRect( &rt, 1, 1 );
/* ceClipToRect( hdc, &rt ); */
2003-11-20 17:26:35 +01:00
if ( !isEmpty ) {
index = getPlayerColor(dctx->trayOwner);
SetTextColor( hdc, dctx->globals->appPrefs.colors[index] );
/* For some reason Rectangle isn't using the background brush to
fill, so FillRect has to get called after Rectangle. Need to
call InsetRect either way to put chars in the right place. */
highlighted = (flags & CELL_HIGHLIGHT) != 0;
if ( highlighted ) {
/* draw thicker hilight frame */
Rectangle( hdc, rt.left, rt.top, rt.right, rt.bottom );
InsetRect( &rt, 1, 1 );
}
2006-11-12 16:22:26 +01:00
}
2003-11-20 17:26:35 +01:00
FillRect( hdc, &rt, dctx->brushes[backIndex] );
2006-11-12 16:22:26 +01:00
if ( !isEmpty ) {
/* Dumb to calc these when only needed once.... */
XP_U16 visHt = xprect->height - 5;
XP_U16 valHt = visHt / 3;
XP_U16 charHt = visHt - valHt;
if ( !highlighted ) {
InsetRect( &rt, 1, 1 );
}
if ( !!letters ) {
XP_U16 offset, actualHt;
HFONT font = ceGetSizedFont( dctx, charHt,
RFONTS_TRAY, &offset, &actualHt );
HFONT oldFont = SelectObject( hdc, font );
MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, letters, -1,
widebuf,
VSIZE(widebuf) );
ceDrawTextClipped( hdc, widebuf, -1, XP_TRUE, xprect->left + 4,
xprect->top + 4, xprect->width, actualHt,
offset, DT_LEFT );
SelectObject( hdc, oldFont );
} else if ( !!bitmap ) {
RECT lrt = rt;
XP_U16 tmp = CE_USER_COLOR1+dctx->trayOwner;
++lrt.left;
lrt.top += 4;
makeAndDrawBitmap( dctx, hdc, &lrt, XP_FALSE,
dctx->globals->appPrefs.colors[tmp],
(CEBitmapInfo*)bitmap );
}
if ( val >= 0 ) {
XP_U16 offset, actualHt;
HFONT font = ceGetSizedFont( dctx, valHt, RFONTS_TRAYVAL,
&offset, &actualHt );
HFONT oldFont = SelectObject( hdc, font );
swprintf( widebuf, L"%d", val );
ceDrawTextClipped( hdc, widebuf, -1, XP_TRUE,
xprect->left + 4,
xprect->top + xprect->height - 4 - actualHt,
xprect->width - 8, actualHt,
offset, DT_RIGHT );
SelectObject( hdc, oldFont );
}
2006-11-12 16:22:26 +01:00
}
2003-11-20 17:26:35 +01:00
}
} /* drawDrawTileGuts */
DLSTATIC void
DRAW_FUNC_NAME(drawTile)( DrawCtx* p_dctx, const XP_Rect* xprect,
const XP_UCHAR* letters, XP_Bitmap bitmap,
2006-11-12 16:22:26 +01:00
XP_S16 val, CellFlags flags )
2003-11-20 17:26:35 +01:00
{
2006-11-12 16:22:26 +01:00
drawDrawTileGuts( p_dctx, xprect, letters, bitmap, val, flags );
2003-11-20 17:26:35 +01:00
} /* ce_draw_drawTile */
#ifdef POINTER_SUPPORT
DLSTATIC void
DRAW_FUNC_NAME(drawTileMidDrag)( DrawCtx* p_dctx, const XP_Rect* xprect,
const XP_UCHAR* letters, XP_Bitmap bitmap,
XP_S16 val, XP_U16 owner, CellFlags flags )
{
draw_trayBegin( p_dctx, xprect, owner, DFS_NONE );
drawDrawTileGuts( p_dctx, xprect, letters, bitmap, val, flags );
} /* ce_draw_drawTile */
#endif
DLSTATIC void
2006-11-12 16:22:26 +01:00
DRAW_FUNC_NAME(drawTileBack)( DrawCtx* p_dctx, const XP_Rect* xprect,
CellFlags flags )
2003-11-20 17:26:35 +01:00
{
2006-11-12 16:22:26 +01:00
drawDrawTileGuts( p_dctx, xprect, "?", NULL, -1, flags );
2003-11-20 17:26:35 +01:00
} /* ce_draw_drawTileBack */
DLSTATIC void
DRAW_FUNC_NAME(drawTrayDivider)( DrawCtx* p_dctx, const XP_Rect* rect,
CellFlags flags )
2003-11-20 17:26:35 +01:00
{
CEDrawCtx* dctx = (CEDrawCtx*)p_dctx;
CEAppGlobals* globals = dctx->globals;
HDC hdc = globals->hdc;
RECT rt;
XP_Bool selected = (flags & CELL_HIGHLIGHT) != 0;
XP_Bool isFocussed = TREAT_AS_CURSOR(dctx,flags);
2003-11-20 17:26:35 +01:00
XPRtoRECT( &rt, rect );
ceClipToRect( hdc, &rt );
if ( isFocussed ) {
FillRect( hdc, &rt, dctx->brushes[CE_FOCUS_COLOR] );
InsetRect( &rt, 0, (rt.bottom - rt.top) >> 2 );
}
2003-11-20 17:26:35 +01:00
if ( selected ) {
Rectangle( hdc, rt.left, rt.top, rt.right, rt.bottom );
} else {
FillRect( hdc, &rt, dctx->brushes[CE_BLACK_COLOR] );
2003-11-20 17:26:35 +01:00
}
} /* ce_draw_drawTrayDivider */
static void
ceClearToBkground( CEDrawCtx* dctx, const XP_Rect* rect )
2003-11-20 17:26:35 +01:00
{
CEAppGlobals* globals = dctx->globals;
HDC hdc = globals->hdc;
RECT rt;
XPRtoRECT( &rt, rect );
FillRect( hdc, &rt, dctx->brushes[CE_BKG_COLOR] );
2003-11-20 17:26:35 +01:00
} /* ceClearToBkground */
static void
ceDrawBitmapInRect( HDC hdc, const RECT* rect, HBITMAP bitmap )
2003-11-20 17:26:35 +01:00
{
BITMAP bmp;
HDC tmpDC;
int nBytes;
int x = rect->left;
int y = rect->top;
2003-11-20 17:26:35 +01:00
tmpDC = CreateCompatibleDC( hdc );
SelectObject( tmpDC, bitmap );
(void)IntersectClipRect( tmpDC, x, y, rect->right, rect->bottom );
2003-11-20 17:26:35 +01:00
nBytes = GetObject( bitmap, sizeof(bmp), &bmp );
XP_ASSERT( nBytes > 0 );
2004-01-22 04:20:19 +01:00
if ( nBytes == 0 ) {
2004-01-30 06:45:23 +01:00
logLastError( "ceDrawBitmapInRect:GetObject" );
2004-01-22 04:20:19 +01:00
}
2003-11-20 17:26:35 +01:00
x += ((rect->right - x) - bmp.bmWidth) / 2;
y += ((rect->bottom - y) - bmp.bmHeight) / 2;
2003-11-20 17:26:35 +01:00
BitBlt( hdc, x, y, bmp.bmWidth, bmp.bmHeight,
tmpDC, 0, 0, SRCCOPY ); /* BLACKNESS */
DeleteDC( tmpDC );
} /* ceDrawBitmapInRect */
DLSTATIC void
DRAW_FUNC_NAME(drawBoardArrow)( DrawCtx* p_dctx, const XP_Rect* xprect,
XWBonusType cursorBonus, XP_Bool vertical,
HintAtts hintAtts, CellFlags flags )
2003-11-20 17:26:35 +01:00
{
CEDrawCtx* dctx = (CEDrawCtx*)p_dctx;
CEAppGlobals* globals = dctx->globals;
HDC hdc = globals->hdc;
RECT rt;
XP_U16 bkIndex;
HBITMAP cursor;
XPRtoRECT( &rt, xprect );
++rt.bottom;
++rt.right;
ceClipToRect( hdc, &rt );
2003-11-20 17:26:35 +01:00
Rectangle( hdc, rt.left, rt.top, rt.right, rt.bottom );
InsetRect( &rt, 1, 1 );
ceClipToRect( hdc, &rt );
2003-11-20 17:26:35 +01:00
if ( vertical ) {
cursor = dctx->downArrow;
} else {
cursor = dctx->rightArrow;
}
if ( TREAT_AS_CURSOR( dctx, flags ) ) {
bkIndex = CE_FOCUS_COLOR;
} else if ( cursorBonus == BONUS_NONE ) {
bkIndex = CE_BKG_COLOR;
2003-11-20 17:26:35 +01:00
} else {
bkIndex = cursorBonus - BONUS_DOUBLE_LETTER + CE_BONUS1_COLOR;
2003-11-20 17:26:35 +01:00
}
FillRect( hdc, &rt, dctx->brushes[bkIndex] );
2004-03-28 03:14:34 +02:00
SetBkColor( hdc, dctx->globals->appPrefs.colors[bkIndex] );
SetTextColor( hdc, dctx->globals->appPrefs.colors[CE_BLACK_COLOR] );
2003-11-20 17:26:35 +01:00
ceDrawBitmapInRect( hdc, &rt, cursor );
2003-11-20 17:26:35 +01:00
2004-12-12 18:39:59 +01:00
ceDrawHintBorders( hdc, xprect, hintAtts );
2003-11-20 17:26:35 +01:00
} /* ce_draw_drawBoardArrow */
DLSTATIC void
DRAW_FUNC_NAME(scoreBegin)( DrawCtx* p_dctx, const XP_Rect* rect,
XP_U16 XP_UNUSED(numPlayers),
2006-11-12 16:22:26 +01:00
DrawFocusState XP_UNUSED(dfs) )
2003-11-20 17:26:35 +01:00
{
CEDrawCtx* dctx = (CEDrawCtx*)p_dctx;
CEAppGlobals* globals = dctx->globals;
XP_ASSERT( !!globals->hdc );
SetBkColor( globals->hdc, dctx->globals->appPrefs.colors[CE_BKG_COLOR] );
2003-11-20 17:26:35 +01:00
dctx->scoreIsVertical = rect->height > rect->width;
/* I don't think the clip rect's set at this point but drawing seems fine
anyway.... ceClearToBkground() is definitely needed here. */
2003-11-20 17:26:35 +01:00
ceClearToBkground( (CEDrawCtx*)p_dctx, rect );
} /* ce_draw_scoreBegin */
static void
formatRemText( XP_S16 nTilesLeft, XP_Bool isVertical, XP_UCHAR* buf )
2003-11-20 17:26:35 +01:00
{
char* fmt;
XP_ASSERT( nTilesLeft > 0 );
if ( isVertical ) {
fmt = "Rem" XP_CR "%d";
} else {
fmt = "Rem:%d";
2003-11-20 17:26:35 +01:00
}
sprintf( buf, fmt, nTilesLeft );
2003-11-20 17:26:35 +01:00
} /* formatRemText */
DLSTATIC void
DRAW_FUNC_NAME(measureRemText)( DrawCtx* p_dctx, const XP_Rect* xprect,
XP_S16 nTilesLeft,
XP_U16* width, XP_U16* height )
2003-11-20 17:26:35 +01:00
{
CEDrawCtx* dctx = (CEDrawCtx*)p_dctx;
if ( nTilesLeft > 0 ) {
2008-03-31 01:58:04 +02:00
XP_UCHAR buf[16];
formatRemText( nTilesLeft, dctx->scoreIsVertical, buf );
2008-03-31 01:58:04 +02:00
ceMeasureText( dctx, buf, CE_REM_PADDING, width, height );
if ( *width > xprect->width ) {
*width = xprect->width;
}
} else {
*width = *height = 0;
}
2003-11-20 17:26:35 +01:00
} /* ce_draw_measureRemText */
DLSTATIC void
DRAW_FUNC_NAME(drawRemText)( DrawCtx* p_dctx, const XP_Rect* rInner,
const XP_Rect* XP_UNUSED(rOuter),
XP_S16 nTilesLeft )
2003-11-20 17:26:35 +01:00
{
CEDrawCtx* dctx = (CEDrawCtx*)p_dctx;
CEAppGlobals* globals = dctx->globals;
HDC hdc = globals->hdc;
XP_UCHAR buf[16];
2003-11-20 17:26:35 +01:00
RECT rt;
formatRemText( nTilesLeft, dctx->scoreIsVertical, buf );
2003-11-20 17:26:35 +01:00
XPRtoRECT( &rt, rInner );
ceClipToRect( hdc, &rt );
2004-01-10 19:02:10 +01:00
++rt.left; /* 1: don't write up against edge */
rt.top -= 2;
drawTextLines( dctx, hdc, buf, CE_REM_PADDING, &rt,
DT_SINGLELINE | DT_LEFT | DT_VCENTER/* | DT_CENTER*/ );
2003-11-20 17:26:35 +01:00
} /* ce_draw_drawRemText */
static void
ceWidthAndText( CEDrawCtx* dctx, const DrawScoreInfo* dsi,
XP_UCHAR* buf, XP_U16* widthP, XP_U16* heightP )
2003-11-20 17:26:35 +01:00
{
XP_UCHAR bullet[] = {'<EFBFBD>', '\0'};
XP_UCHAR tmp[16];
/* For a horizontal scoreboard, we want *300:6*
* For a vertical, it's
*
* 300
* 6
*
* with IS_TURN_VPAD-height rects above and below
*/
if ( !dsi->isTurn || dctx->scoreIsVertical ) {
bullet[0] = '\0';
2003-11-20 17:26:35 +01:00
}
strcpy( buf, bullet );
sprintf( tmp, "%d", dsi->totalScore );
strcat( buf, tmp );
if ( dsi->nTilesLeft >= 0 ) {
strcat( buf, dctx->scoreIsVertical? XP_CR : ":" );
sprintf( tmp, "%d", dsi->nTilesLeft );
strcat( buf, tmp );
2003-11-20 17:26:35 +01:00
}
strcat( buf, bullet );
2008-03-31 01:58:04 +02:00
ceMeasureText( dctx, buf, CE_SCORE_PADDING, widthP, heightP );
if ( dsi->isTurn && dctx->scoreIsVertical ) {
*heightP += IS_TURN_VPAD * 2;
}
2003-11-20 17:26:35 +01:00
} /* ceWidthAndText */
DLSTATIC void
DRAW_FUNC_NAME(measureScoreText)( DrawCtx* p_dctx, const XP_Rect* XP_UNUSED(r),
const DrawScoreInfo* dsi,
XP_U16* widthP, XP_U16* heightP )
2003-11-20 17:26:35 +01:00
{
CEDrawCtx* dctx = (CEDrawCtx*)p_dctx;
CEAppGlobals* globals = dctx->globals;
HDC hdc = globals->hdc;
XP_UCHAR buf[32];
2003-11-20 17:26:35 +01:00
HFONT newFont;
HFONT oldFont;
if ( dsi->selected ) {
newFont = dctx->selPlayerFont;
} else {
newFont = dctx->playerFont;
}
oldFont = SelectObject( hdc, newFont );
ceWidthAndText( dctx, dsi, buf, widthP, heightP );
2003-11-20 17:26:35 +01:00
SelectObject( hdc, oldFont );
} /* ce_draw_measureScoreText */
DLSTATIC void
DRAW_FUNC_NAME(score_drawPlayer)( DrawCtx* p_dctx,
const XP_Rect* rInner,
const XP_Rect* rOuter,
const DrawScoreInfo* dsi )
2003-11-20 17:26:35 +01:00
{
CEDrawCtx* dctx = (CEDrawCtx*)p_dctx;
CEAppGlobals* globals = dctx->globals;
HDC hdc = globals->hdc;
RECT rt;
XP_U16 width, height;
XP_UCHAR scoreBuf[20];
2003-11-20 17:26:35 +01:00
HFONT newFont;
HFONT oldFont;
XP_Bool frameScore;
XP_Bool isFocussed = (dsi->flags & CELL_ISCURSOR) != 0;
XP_U16 bkIndex = isFocussed ? CE_FOCUS_COLOR : CE_BKG_COLOR;
2003-11-20 17:26:35 +01:00
if ( dsi->selected ) {
newFont = dctx->selPlayerFont;
} else {
newFont = dctx->playerFont;
}
oldFont = SelectObject( hdc, newFont );
SetTextColor( hdc, dctx->globals->
appPrefs.colors[getPlayerColor(dsi->playerNum)] );
SetBkColor( hdc, dctx->globals->appPrefs.colors[bkIndex] );
XPRtoRECT( &rt, rOuter );
ceClipToRect( hdc, &rt );
if ( isFocussed ) {
FillRect( hdc, &rt, dctx->brushes[CE_FOCUS_COLOR] );
}
2003-11-20 17:26:35 +01:00
ceWidthAndText( dctx, dsi, scoreBuf, &width, &height );
XPRtoRECT( &rt, rInner );
++rt.top; /* tweak for ce */
frameScore = dsi->isTurn && dctx->scoreIsVertical;
if ( frameScore ) {
rt.top += IS_TURN_VPAD;
rt.bottom -= IS_TURN_VPAD;
}
drawTextLines( dctx, hdc, scoreBuf, CE_SCORE_PADDING, &rt,
DT_SINGLELINE | DT_VCENTER | DT_CENTER );
2003-11-20 17:26:35 +01:00
if ( frameScore ) {
Rectangle( hdc, rt.left, rt.top-IS_TURN_VPAD, rt.right, rt.top );
Rectangle( hdc, rt.left, rt.bottom, rt.right,
rt.bottom + IS_TURN_VPAD );
}
2003-11-20 17:26:35 +01:00
SelectObject( hdc, oldFont );
} /* ce_draw_score_drawPlayer */
DLSTATIC void
DRAW_FUNC_NAME(score_pendingScore)( DrawCtx* p_dctx, const XP_Rect* xprect,
XP_S16 score, XP_U16 playerNum,
CellFlags flags )
2003-11-20 17:26:35 +01:00
{
# define PTS_OFFSET 2
2003-11-20 17:26:35 +01:00
CEDrawCtx* dctx = (CEDrawCtx*)p_dctx;
CEAppGlobals* globals = dctx->globals;
HDC hdc = globals->hdc;
XP_U16 offset, actualHt;
2003-11-20 17:26:35 +01:00
wchar_t widebuf[5];
XP_UCHAR buf[5];
RECT rt;
XP_Bool focussed = TREAT_AS_CURSOR(dctx,flags);
XP_U16 bkIndex = focussed ? CE_FOCUS_COLOR : CE_BKG_COLOR;
2003-11-20 17:26:35 +01:00
HFONT font = ceGetSizedFont( dctx,
(xprect->height-(2*PTS_OFFSET))/2,
RFONTS_PTS, &offset, &actualHt );
HFONT oldFont = SelectObject( hdc, font );
SetTextColor( hdc,
dctx->globals->appPrefs.colors[getPlayerColor(playerNum)] );
SetBkColor( hdc, dctx->globals->appPrefs.colors[bkIndex] );
2003-11-20 17:26:35 +01:00
XPRtoRECT( &rt, xprect );
ceClipToRect( hdc, &rt );
FillRect( hdc, &rt, dctx->brushes[bkIndex] );
2003-11-20 17:26:35 +01:00
ceDrawTextClipped( hdc, L"Pts", -1, XP_FALSE,
xprect->left + PTS_OFFSET, xprect->top + PTS_OFFSET,
xprect->width - (PTS_OFFSET*2), actualHt, offset,
DT_CENTER );
2003-11-20 17:26:35 +01:00
if ( score < 0 ) {
buf[0] = '?';
buf[1] = '?';
buf[2] = '\0';
} else {
sprintf( buf, "%d", score );
}
MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, buf, -1,
widebuf, VSIZE(widebuf) );
ceDrawTextClipped( hdc, widebuf, -1, XP_TRUE, xprect->left + PTS_OFFSET,
xprect->top + xprect->height - PTS_OFFSET - actualHt,
xprect->width - (PTS_OFFSET*2), actualHt, offset,
DT_CENTER );
SelectObject( hdc, oldFont );
# undef PTS_OFFSET
2003-11-20 17:26:35 +01:00
} /* ce_draw_score_pendingScore */
DLSTATIC void
DRAW_FUNC_NAME(drawTimer)( DrawCtx* p_dctx, const XP_Rect* rInner,
const XP_Rect* XP_UNUSED(rOuter),
XP_U16 player, XP_S16 secondsLeft )
2003-11-20 17:26:35 +01:00
{
CEDrawCtx* dctx = (CEDrawCtx*)p_dctx;
CEAppGlobals* globals = dctx->globals;
HDC hdc = globals->hdc;
XP_UCHAR buf[16];
2003-11-20 17:26:35 +01:00
XP_U16 mins, secs;
RECT rt;
PAINTSTRUCT ps;
XP_Bool isNegative;
XPRtoRECT( &rt, rInner );
isNegative = secondsLeft < 0;
if ( isNegative ) {
secondsLeft *= -1;
}
mins = secondsLeft / 60;
secs = secondsLeft % 60;
snprintf( buf, sizeof(buf),
dctx->scoreIsVertical? "%s%.1dm" XP_CR "%.2ds" : "%s%.1d:%.2d",
isNegative? "-": "", mins, secs );
2003-11-20 17:26:35 +01:00
if ( !hdc ) {
2003-11-20 17:26:35 +01:00
InvalidateRect( dctx->mainWin, &rt, FALSE );
hdc = BeginPaint( dctx->mainWin, &ps );
}
ceClipToRect( hdc, &rt );
2004-03-28 03:14:34 +02:00
SetTextColor( hdc, dctx->globals->appPrefs.colors[getPlayerColor(player)] );
SetBkColor( hdc, dctx->globals->appPrefs.colors[CE_BKG_COLOR] );
2003-11-20 17:26:35 +01:00
ceClearToBkground( dctx, rInner );
drawTextLines( dctx, hdc, buf, CE_TIMER_PADDING, &rt,
DT_SINGLELINE | DT_VCENTER | DT_CENTER);
2003-11-20 17:26:35 +01:00
if ( !globals->hdc ) {
EndPaint( dctx->mainWin, &ps );
}
} /* ce_draw_drawTimer */
DLSTATIC const XP_UCHAR*
DRAW_FUNC_NAME(getMiniWText)( DrawCtx* XP_UNUSED(p_dctx),
XWMiniTextType whichText )
2003-11-20 17:26:35 +01:00
{
XP_UCHAR* str;
switch( whichText ) {
case BONUS_DOUBLE_LETTER:
2006-05-28 18:16:46 +02:00
str = "Double letter";
break;
2003-11-20 17:26:35 +01:00
case BONUS_DOUBLE_WORD:
2006-05-28 18:16:46 +02:00
str = "Double word";
break;
2003-11-20 17:26:35 +01:00
case BONUS_TRIPLE_LETTER:
2006-05-28 18:16:46 +02:00
str = "Triple letter";
break;
2003-11-20 17:26:35 +01:00
case BONUS_TRIPLE_WORD:
2006-05-28 18:16:46 +02:00
str = "Triple word";
break;
2003-11-20 17:26:35 +01:00
case INTRADE_MW_TEXT:
str = "Trading tiles." XP_CR "Select 'Turn done' when ready";
2006-05-28 18:16:46 +02:00
break;
2003-11-20 17:26:35 +01:00
default:
XP_ASSERT( XP_FALSE );
2006-05-28 18:16:46 +02:00
str = NULL;
break;
2003-11-20 17:26:35 +01:00
}
return str;
} /* ce_draw_getMiniWText */
DLSTATIC void
DRAW_FUNC_NAME(measureMiniWText)( DrawCtx* p_dctx, const XP_UCHAR* str,
XP_U16* widthP, XP_U16* heightP )
2003-11-20 17:26:35 +01:00
{
CEDrawCtx* dctx = (CEDrawCtx*)p_dctx;
2008-03-31 01:58:04 +02:00
ceMeasureText( dctx, str, CE_MINIW_PADDING, widthP, heightP );
2006-05-11 04:58:36 +02:00
*heightP += CE_MINI_V_PADDING;
2003-11-20 17:26:35 +01:00
} /* ce_draw_measureMiniWText */
DLSTATIC void
DRAW_FUNC_NAME(drawMiniWindow)( DrawCtx* p_dctx, const XP_UCHAR* text,
const XP_Rect* rect,
void** XP_UNUSED(closureP) )
2003-11-20 17:26:35 +01:00
{
CEDrawCtx* dctx = (CEDrawCtx*)p_dctx;
CEAppGlobals* globals = dctx->globals;
HDC hdc;
2004-02-28 06:25:21 +01:00
RECT rt, textRt;
2003-11-20 17:26:35 +01:00
PAINTSTRUCT ps;
XPRtoRECT( &rt, rect );
if ( !!globals->hdc ) {
hdc = globals->hdc;
} else {
2003-11-20 17:26:35 +01:00
InvalidateRect( dctx->mainWin, &rt, FALSE );
hdc = BeginPaint( dctx->mainWin, &ps );
}
ceClipToRect( hdc, &rt );
2003-11-20 17:26:35 +01:00
2008-03-31 01:58:04 +02:00
ceClearToBkground( dctx, rect );
2003-11-20 17:26:35 +01:00
SetBkColor( hdc, dctx->globals->appPrefs.colors[CE_BKG_COLOR] );
SetTextColor( hdc, dctx->globals->appPrefs.colors[CE_BLACK_COLOR] );
2004-02-28 06:25:21 +01:00
2003-11-20 17:26:35 +01:00
Rectangle( hdc, rt.left, rt.top, rt.right, rt.bottom );
InsetRect( &rt, 1, 1 );
Rectangle( hdc, rt.left, rt.top, rt.right, rt.bottom );
2004-02-28 06:25:21 +01:00
textRt = rt;
textRt.top += 2;
InsetRect( &textRt, 3, 0 );
drawTextLines( dctx, hdc, text, CE_MINIW_PADDING, &textRt,
DT_CENTER | DT_VCENTER );
2003-11-20 17:26:35 +01:00
if ( !globals->hdc ) {
EndPaint( dctx->mainWin, &ps );
}
} /* ce_draw_drawMiniWindow */
DLSTATIC void
DRAW_FUNC_NAME(destroyCtxt)( DrawCtx* p_dctx )
2003-11-20 17:26:35 +01:00
{
XP_U16 i;
CEDrawCtx* dctx = (CEDrawCtx*)p_dctx;
for ( i = 0; i < CE_NUM_COLORS; ++i ) {
2003-11-20 17:26:35 +01:00
DeleteObject( dctx->brushes[i] );
if ( !!dctx->pens[i].pen ) {
DeleteObject( dctx->pens[i].pen );
}
2003-11-20 17:26:35 +01:00
}
DeleteObject( dctx->playerFont );
DeleteObject( dctx->selPlayerFont );
ceClearFontCache( dctx );
2003-11-20 17:26:35 +01:00
DeleteObject( dctx->rightArrow );
DeleteObject( dctx->downArrow );
DeleteObject( dctx->origin );
#ifndef DRAW_LINK_DIRECT
2003-11-20 17:26:35 +01:00
XP_FREE( dctx->mpool, p_dctx->vtable );
#endif
2003-11-20 17:26:35 +01:00
XP_FREE( dctx->mpool, dctx );
} /* ce_draw_destroyCtxt */
DLSTATIC void
DRAW_FUNC_NAME(dictChanged)( DrawCtx* p_dctx, const DictionaryCtxt* dict )
{
CEDrawCtx* dctx = (CEDrawCtx*)p_dctx;
dctx->dict = dict;
}
#ifdef DRAW_LINK_DIRECT
DLSTATIC XP_Bool
DRAW_FUNC_NAME(vertScrollBoard)( DrawCtx* p_dctx, XP_Rect* rect,
XP_S16 dist, DrawFocusState XP_UNUSED(dfs) )
{
2006-06-24 03:22:10 +02:00
XP_Bool success = XP_FALSE;
/* board passes in the whole board rect, so we need to subtract from it
the height of the area to be overwritten. If dist is negative, the
dest is above the src. Otherwise it's below. */
2006-06-24 03:22:10 +02:00
CEDrawCtx* dctx = (CEDrawCtx*)p_dctx;
CEAppGlobals* globals = dctx->globals;
int destY, srcY;
RECT rt;
2006-06-24 03:22:10 +02:00
XP_Bool down = dist <= 0;
XPRtoRECT( &rt, rect );
ceClipToRect( globals->hdc, &rt );
2006-06-24 03:22:10 +02:00
if ( down ) {
srcY = rect->top;
dist = -dist; /* make it positive */
destY = srcY + dist;
} else {
destY = rect->top;
srcY = destY + dist;
}
success = FALSE != BitBlt( globals->hdc, /* HDC hdcDest, */
rect->left, /* int nXDest */
destY,
rect->width, /* width */
rect->height - dist, /* int nHeight */
globals->hdc, /* HDC hdcSrc */
rect->left, /* int nXSrc */
srcY,
SRCCOPY ); /* DWORD dwRop */
/* need to return the rect that must still be redrawn */
if ( success ) {
if ( !down ) {
rect->top += rect->height - dist;
}
2006-06-24 03:22:10 +02:00
rect->height = dist;
}
2006-06-24 03:22:10 +02:00
return success;
}
#else /* #ifdef DRAW_LINK_DIRECT */
2003-11-20 17:26:35 +01:00
static void
ce_draw_doNothing( DrawCtx* dctx, ... )
2003-11-20 17:26:35 +01:00
{
} /* ce_draw_doNothing */
#endif
2003-11-20 17:26:35 +01:00
static void
ceFontsSetup( CEDrawCtx* dctx )
2003-11-20 17:26:35 +01:00
{
LOGFONT font;
XP_MEMSET( &font, 0, sizeof(font) );
wcscpy( font.lfFaceName, L"Arial" );
2003-11-20 17:26:35 +01:00
font.lfWeight = FW_LIGHT;
font.lfHeight = CE_SCORE_HEIGHT;
2003-11-20 17:26:35 +01:00
dctx->playerFont = CreateFontIndirect( &font );
font.lfWeight = FW_BOLD;
font.lfHeight = CE_SCORE_HEIGHT+2;
2003-11-20 17:26:35 +01:00
dctx->selPlayerFont = CreateFontIndirect( &font );
} /* ceFontsSetup */
2004-03-28 03:14:34 +02:00
void
ce_draw_update( CEDrawCtx* dctx )
2003-11-20 17:26:35 +01:00
{
XP_U16 i;
2004-03-28 03:14:34 +02:00
for ( i = 0; i < CE_NUM_COLORS; ++i ) {
2004-03-28 03:14:34 +02:00
if ( !!dctx->brushes[i] ) {
DeleteObject( dctx->brushes[i] );
}
dctx->brushes[i] = CreateSolidBrush(dctx->globals->appPrefs.colors[i]);
2003-11-20 17:26:35 +01:00
}
2004-03-28 03:14:34 +02:00
} /* ce_drawctxt_update */
2003-11-20 17:26:35 +01:00
void
ce_draw_erase( CEDrawCtx* dctx, const RECT* invalR )
{
CEAppGlobals* globals = dctx->globals;
FillRect( globals->hdc, invalR, dctx->brushes[CE_BKG_COLOR] );
}
CEDrawCtx*
2003-11-20 17:26:35 +01:00
ce_drawctxt_make( MPFORMAL HWND mainWin, CEAppGlobals* globals )
{
CEDrawCtx* dctx = (CEDrawCtx*)XP_MALLOC( mpool,
sizeof(*dctx) );
XP_MEMSET( dctx, 0, sizeof(*dctx) );
MPASSIGN(dctx->mpool, mpool);
2003-11-20 17:26:35 +01:00
#ifndef DRAW_LINK_DIRECT
2003-11-20 17:26:35 +01:00
dctx->vtable = (DrawCtxVTable*)XP_MALLOC( mpool, sizeof(*((dctx)->vtable)));
for ( i = 0; i < sizeof(*dctx->vtable)/4; ++i ) {
((void**)(dctx->vtable))[i] = ce_draw_doNothing;
2003-11-20 17:26:35 +01:00
}
SET_VTABLE_ENTRY( dctx->vtable, draw_destroyCtxt, ce );
SET_VTABLE_ENTRY( dctx->vtable, draw_dictChanged, ce );
2003-11-20 17:26:35 +01:00
SET_VTABLE_ENTRY( dctx->vtable, draw_boardBegin, ce );
SET_VTABLE_ENTRY( dctx->vtable, draw_drawCell, ce );
SET_VTABLE_ENTRY( dctx->vtable, draw_invertCell, ce );
SET_VTABLE_ENTRY( dctx->vtable, draw_trayBegin, ce );
SET_VTABLE_ENTRY( dctx->vtable, draw_drawTile, ce );
SET_VTABLE_ENTRY( dctx->vtable, draw_drawTileBack, ce );
SET_VTABLE_ENTRY( dctx->vtable, draw_drawTrayDivider, ce );
SET_VTABLE_ENTRY( dctx->vtable, draw_clearRect, ce );
SET_VTABLE_ENTRY( dctx->vtable, draw_drawBoardArrow, ce );
SET_VTABLE_ENTRY( dctx->vtable, draw_scoreBegin, ce );
SET_VTABLE_ENTRY( dctx->vtable, draw_measureRemText, ce );
SET_VTABLE_ENTRY( dctx->vtable, draw_drawRemText, ce );
SET_VTABLE_ENTRY( dctx->vtable, draw_measureScoreText, ce );
SET_VTABLE_ENTRY( dctx->vtable, draw_score_drawPlayer, ce );
SET_VTABLE_ENTRY( dctx->vtable, draw_score_pendingScore, ce );
SET_VTABLE_ENTRY( dctx->vtable, draw_objFinished, ce );
2003-11-20 17:26:35 +01:00
SET_VTABLE_ENTRY( dctx->vtable, draw_drawTimer, ce );
SET_VTABLE_ENTRY( dctx->vtable, draw_getMiniWText, ce );
SET_VTABLE_ENTRY( dctx->vtable, draw_measureMiniWText, ce );
SET_VTABLE_ENTRY( dctx->vtable, draw_drawMiniWindow, ce );
#endif
2003-11-20 17:26:35 +01:00
dctx->mainWin = mainWin;
dctx->globals = globals;
ce_draw_update( dctx );
2003-11-20 17:26:35 +01:00
ceFontsSetup( dctx );
2003-11-20 17:26:35 +01:00
dctx->rightArrow = LoadBitmap( globals->hInst,
MAKEINTRESOURCE(IDB_RIGHTARROW) );
dctx->downArrow = LoadBitmap( globals->hInst,
MAKEINTRESOURCE(IDB_DOWNARROW) );
dctx->origin = LoadBitmap( globals->hInst,
MAKEINTRESOURCE(IDB_ORIGIN) );
return dctx;
2003-11-20 17:26:35 +01:00
} /* ce_drawctxt_make */