xwords/xwords4/franklin/frankdraw.cpp

638 lines
18 KiB
C++

/* -*-mode: C; fill-column: 78; c-basic-offset: 4;-*- */
/*
* Copyright 1997-2001 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 <stdlib.h>
extern "C" {
#include "xptypes.h"
#include "board.h"
#include "draw.h"
#include "mempool.h"
} /* extern "C" */
#include "frankmain.h"
static void
insetRect( RECT* r, short i )
{
r->x += i;
r->y += i;
i *= 2;
r->width -= i;
r->height -= i;
} /* insetRect */
static void
eraseRect(FrankDrawCtx* dctx, RECT* rect )
{
dctx->window->DrawRectFilled( rect, COLOR_WHITE );
} /* eraseRect */
static XP_Bool
frank_draw_boardBegin( DrawCtx* p_dctx, const DictionaryCtxt* dict,
const XP_Rect* rect, XP_Bool hasfocus )
{
return XP_TRUE;
} /* draw_finish */
static void
frank_draw_boardFinished( DrawCtx* p_dctx )
{
// GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
} /* draw_finished */
static void
XP_RectToRECT( RECT* d, const XP_Rect* s )
{
d->x = s->left;
d->y = s->top;
d->width = s->width;
d->height = s->height;
} /* XP_RectToRECT */
/* Called by methods that draw cells, i.e. drawCell and drawCursor, to do
* common prep of the area.
*/
static void
cellDrawPrep( FrankDrawCtx* dctx, const XP_Rect* xprect, RECT* insetRect )
{
XP_RectToRECT( insetRect, xprect );
++insetRect->height;
++insetRect->width;
eraseRect( dctx, insetRect );
} /* cellDrawPrep */
static XP_Bool
frank_draw_drawCell( DrawCtx* p_dctx, const XP_Rect* xprect,
const XP_UCHAR* letters, const XP_Bitmap* bitmap,
Tile tile, XP_S16 owner, XWBonusType bonus,
HintAtts hintAtts,
XP_Bool isBlank, XP_Bool isPending, XP_Bool isStar )
{
FrankDrawCtx* dctx = (FrankDrawCtx*)p_dctx;
RECT rectInset;
XP_Bool showGrid = XP_TRUE;
cellDrawPrep( dctx, xprect, &rectInset );
if ( !!letters ) {
if ( *letters == LETTER_NONE ) {
if ( bonus != BONUS_NONE ) {
XP_ASSERT( bonus <= 4 );
#ifdef USE_PATTERNS
dctx->window->DrawTiledImage( xprect->left, xprect->top,
xprect->width,
xprect->height,
&dctx->bonusImages[bonus-1] );
#else
RECT filledR = rectInset;
COLOR color;
insetRect( &filledR, 1 );
switch( bonus ) {
case BONUS_DOUBLE_LETTER:
color = COLOR_GRAY53;
break;
case BONUS_TRIPLE_LETTER:
color = COLOR_GRAY40;
break;
case BONUS_DOUBLE_WORD:
color = COLOR_GRAY27;
break;
case BONUS_TRIPLE_WORD:
color = COLOR_GRAY13;
break;
default:
color = COLOR_WHITE; /* make compiler happy */
XP_ASSERT(0);
}
dctx->window->DrawRectFilled( &filledR, color );
#endif
}
if ( isStar ) {
dctx->window->DrawImage( xprect->left+2, xprect->top+2,
&dctx->startMark );
}
} else {
dctx->window->DrawText( (const char*)letters, rectInset.x+2,
rectInset.y+1 );
}
} else if ( !!bitmap ) {
dctx->window->DrawImage( xprect->left+2, xprect->top+2,
(IMAGE*)bitmap );
}
if ( showGrid ) {
COLOR color = isBlank? COLOR_GRAY53:COLOR_BLACK;
dctx->window->DrawRect( &rectInset, color );
}
if ( isPending ) {
insetRect( &rectInset, 1 );
dctx->window->InvertRect( &rectInset );
}
return XP_TRUE;
} /* frank_draw_drawCell */
static void
frank_draw_invertCell( DrawCtx* p_dctx, const XP_Rect* rect )
{
FrankDrawCtx* dctx = (FrankDrawCtx*)p_dctx;
RECT r;
XP_RectToRECT( &r, rect );
r.x += 3;
r.y += 3;
r.width -= 5;
r.height -= 5;
/* insetRect( &r, 2 ); */
dctx->window->InvertRect( &r );
GUI_UpdateNow();
} /* frank_draw_invertCell */
static XP_Bool
frank_draw_trayBegin( DrawCtx* p_dctx, const XP_Rect* rect, XP_U16 owner,
XP_Bool hasfocus )
{
/* FrankDrawCtx* dctx = (FrankDrawCtx*)p_dctx; */
/* clip? */
/* eraseRect( dctx, rect ); */
return XP_TRUE;
} /* frank_draw_trayBegin */
static void
frank_draw_drawTile( DrawCtx* p_dctx, const XP_Rect* xprect,
const XP_UCHAR* letter,
XP_Bitmap* bitmap, XP_S16 val, XP_Bool highlighted )
{
FrankDrawCtx* dctx = (FrankDrawCtx*)p_dctx;
char numbuf[3];
U32 width;
XP_Rect insetR = *xprect;
RECT rect;
XP_RectToRECT( &rect, xprect );
eraseRect( dctx, &rect );
/* frame the tile */
dctx->window->DrawRect( &rect, COLOR_BLACK );
if ( !!letter ) {
if ( *letter != LETTER_NONE ) { /* blank */
dctx->window->DrawText( (char*)letter, rect.x+1, rect.y+1,
dctx->trayFont );
}
} else if ( !!bitmap ) {
dctx->window->DrawImage( rect.x+2, rect.y+3, (IMAGE*)bitmap );
}
if ( val >= 0 ) {
sprintf( (char*)numbuf, (const char*)"%d", val );
width = GUI_TextWidth( dctx->valFont, numbuf, strlen(numbuf));
U16 height = GUI_FontHeight( dctx->valFont );
dctx->window->DrawText( (char*)numbuf, rect.x+rect.width - width - 1,
rect.y + rect.height - height - 1,
dctx->valFont );
}
if ( highlighted ) {
insetRect( &rect, 1 );
dctx->window->DrawRect( &rect, COLOR_BLACK );
}
} /* frank_draw_drawTile */
static void
frank_draw_drawTileBack( DrawCtx* p_dctx, const XP_Rect* xprect )
{
/* FrankDrawCtx* dctx = (FrankDrawCtx*)p_dctx; */
frank_draw_drawTile( p_dctx, xprect, (XP_UCHAR*)"?",
(XP_Bitmap*)NULL, -1, XP_FALSE );
} /* frank_draw_drawTileBack */
static void
frank_draw_drawTrayDivider( DrawCtx* p_dctx, const XP_Rect* rect,
XP_Bool selected )
{
FrankDrawCtx* dctx = (FrankDrawCtx*)p_dctx;
RECT winRect;
XP_RectToRECT( &winRect, rect );
eraseRect( dctx, &winRect );
++winRect.x;
winRect.width -= 2;
COLOR color;
if ( selected ) {
color = COLOR_GRAY27;
} else {
color = COLOR_BLACK;
}
dctx->window->DrawRectFilled( &winRect, color );
} /* frank_draw_drawTrayDivider */
static void
frank_draw_clearRect( DrawCtx* p_dctx, const XP_Rect* rectP )
{
FrankDrawCtx* dctx = (FrankDrawCtx*)p_dctx;
RECT rect;
XP_RectToRECT( &rect, rectP );
eraseRect( dctx, &rect );
} /* frank_draw_clearRect */
static void
frank_draw_drawBoardArrow( DrawCtx* p_dctx, const XP_Rect* xprect,
XWBonusType cursorBonus, XP_Bool vertical,
HintAtts hintAtts )
{
FrankDrawCtx* dctx = (FrankDrawCtx*)p_dctx;
RECT rect;
cellDrawPrep( dctx, xprect, &rect );
dctx->window->DrawImage( rect.x+3, rect.y+2,
vertical?&dctx->downcursor:&dctx->rightcursor );
/* frame the cell */
dctx->window->DrawRect( &rect, COLOR_BLACK );
} /* frank_draw_drawBoardArrow */
static void
frank_draw_scoreBegin( DrawCtx* p_dctx, const XP_Rect* rect,
XP_U16 numPlayers, XP_Bool hasfocus )
{
FrankDrawCtx* dctx = (FrankDrawCtx*)p_dctx;
RECT r;
XP_RectToRECT( &r, rect );
eraseRect( dctx, &r );
} /* frank_draw_scoreBegin */
static void
frank_draw_measureRemText( DrawCtx* p_dctx, const XP_Rect* r,
XP_S16 nTilesLeft,
XP_U16* width, XP_U16* height )
{
FrankDrawCtx* dctx = (FrankDrawCtx*)p_dctx;
*height = SCORE_HEIGHT;
char buf[15];
sprintf( (char*)buf, "rem:%d", nTilesLeft );
*width = GUI_TextWidth( dctx->scoreFnt, (char*)buf,
strlen(buf) );
} /* frank_draw_measureRemText */
static void
frank_draw_drawRemText( DrawCtx* p_dctx, const XP_Rect* rInner,
const XP_Rect* rOuter, XP_S16 nTilesLeft )
{
FrankDrawCtx* dctx = (FrankDrawCtx*)p_dctx;
char buf[15];
sprintf( (char*)buf, "rem:%d", nTilesLeft );
dctx->window->DrawText( (char*)buf, rInner->left, rInner->top,
dctx->scoreFnt );
} /* frank_draw_drawRemText */
static XP_U16
scoreWidthAndText( char* buf, const FONT* font, const DrawScoreInfo* dsi )
{
char* borders = "";
if ( dsi->isTurn ) {
borders = "*";
}
sprintf( buf, "%s%.3d", borders, dsi->score );
if ( dsi->nTilesLeft >= 0 ) {
char nbuf[10];
sprintf( nbuf, ":%d", dsi->nTilesLeft );
strcat( buf, nbuf );
}
strcat( buf, borders );
S32 width = GUI_TextWidth( font, buf, strlen(buf) );
return width;
} /* scoreWidthAndText */
static void
frank_draw_measureScoreText( DrawCtx* p_dctx, const XP_Rect* r,
const DrawScoreInfo* dsi,
XP_U16* width, XP_U16* height )
{
FrankDrawCtx* dctx = (FrankDrawCtx*)p_dctx;
char buf[20];
const FONT* font = dsi->selected? dctx->scoreFntBold:dctx->scoreFnt;
*height = SCORE_HEIGHT;
*width = scoreWidthAndText( buf, font, dsi );
} /* frank_draw_measureScoreText */
static void
frank_draw_score_drawPlayer( DrawCtx* p_dctx,
const XP_Rect* rInner, const XP_Rect* rOuter,
const DrawScoreInfo* dsi )
{
FrankDrawCtx* dctx = (FrankDrawCtx*)p_dctx;
char buf[20];
XP_U16 x;
const FONT* font = dsi->selected? dctx->scoreFntBold:dctx->scoreFnt;
S32 width = scoreWidthAndText( buf, font, dsi );
x = rInner->left + ((rInner->width - width) /2);
dctx->window->DrawText( buf, x, rInner->top, font );
} /* frank_draw_score_drawPlayer */
static void
frank_draw_score_pendingScore( DrawCtx* p_dctx, const XP_Rect* rect,
XP_S16 score, XP_U16 playerNum )
{
FrankDrawCtx* dctx = (FrankDrawCtx*)p_dctx;
char buf[5];
XP_U16 left;
if ( score >= 0 ) {
sprintf( buf, "%.3d", score );
} else {
strcpy( buf, "???" );
}
RECT r;
XP_RectToRECT( &r, rect );
eraseRect( dctx, &r );
left = r.x+1;
dctx->window->DrawText( "Pts:", left, r.y, dctx->valFont );
dctx->window->DrawText( buf, left, r.y+(r.height/2),
dctx->scoreFnt );
} /* frank_draw_score_pendingScore */
static void
frank_draw_scoreFinished( DrawCtx* p_dctx )
{
} /* frank_draw_scoreFinished */
static U16
frankFormatTimerText( char* buf, XP_S16 secondsLeft )
{
XP_U16 minutes, seconds;
XP_U16 nChars = 0;
if ( secondsLeft < 0 ) {
*buf++ = '-';
secondsLeft *= -1;
++nChars;
}
minutes = secondsLeft / 60;
seconds = secondsLeft % 60;
char secBuf[6];
sprintf( secBuf, "0%d", seconds );
nChars += sprintf( buf, "%d:%s", minutes,
secBuf[2] == '\0'? secBuf:&secBuf[1] );
return nChars;
} /* frankFormatTimerText */
static void
frank_draw_drawTimer( DrawCtx* p_dctx, const XP_Rect* rInner,
const XP_Rect* rOuter, XP_U16 player,
XP_S16 secondsLeft )
{
FrankDrawCtx* dctx = (FrankDrawCtx*)p_dctx;
char buf[12];
(void)frankFormatTimerText( buf, secondsLeft );
XP_DEBUGF( "drawing timer text: %s at %d,%d", buf, rInner->left,
rInner->top );
RECT r;
XP_RectToRECT( &r, rInner );
eraseRect( dctx, &r );
dctx->window->DrawText( buf, rInner->left, rInner->top, dctx->scoreFnt );
} /* frank_draw_drawTimer */
static XP_UCHAR*
frank_draw_getMiniWText( DrawCtx* p_dctx, XWMiniTextType whichText )
{
char* str = (char*)NULL;
switch( whichText ) {
case BONUS_DOUBLE_LETTER:
str = "Double letter"; break;
case BONUS_DOUBLE_WORD:
str = "Double word"; break;
case BONUS_TRIPLE_LETTER:
str = "Triple letter"; break;
case BONUS_TRIPLE_WORD:
str = "Triple word"; break;
case INTRADE_MW_TEXT:
str = "Click D when done"; break;
default:
XP_ASSERT( XP_FALSE );
}
return (XP_UCHAR*)str;
} /* frank_draw_getMiniWText */
static void
frank_draw_measureMiniWText( DrawCtx* p_dctx, const XP_UCHAR* str,
XP_U16* widthP, XP_U16* heightP )
{
FrankDrawCtx* dctx = (FrankDrawCtx*)p_dctx;
*widthP = 6 + GUI_TextWidth( dctx->scoreFnt, (const char*)str,
strlen((const char*)str) );
*heightP = 6 + GUI_FontHeight( dctx->scoreFnt );
} /* frank_draw_measureMiniWText */
static void
frank_draw_drawMiniWindow( DrawCtx* p_dctx, const XP_UCHAR* text,
const XP_Rect* rect, void** closureP )
{
FrankDrawCtx* dctx = (FrankDrawCtx*)p_dctx;
RECT r;
XP_RectToRECT( &r, rect );
eraseRect( dctx, &r );
--r.width;
--r.height;
++r.x;
++r.y;
dctx->window->DrawRect( &r, COLOR_BLACK );
--r.x;
--r.y;
eraseRect( dctx, &r );
dctx->window->DrawRect( &r, COLOR_BLACK );
dctx->window->DrawText( (const char*)text, r.x+2, r.y+2, dctx->scoreFnt );
} /* frank_draw_drawMiniWindow */
static void
frank_draw_eraseMiniWindow( DrawCtx* p_dctx, const XP_Rect* rect,
XP_Bool lastTime,
void** closure, XP_Bool* invalUnder )
{
/* FrankDrawCtx* dctx = (FrankDrawCtx*)p_dctx; */
*invalUnder = XP_TRUE;
} /* frank_draw_eraseMiniWindow */
#define SET_GDK_COLOR( c, r, g, b ) { \
c.red = (r); \
c.green = (g); \
c.blue = (b); \
}
static void
draw_doNothing( DrawCtx* dctx, ... )
{
} /* draw_doNothing */
const unsigned char downcursor_bits[] = {
0x00, 0x00,
0x10, 0x00,
0x10, 0x00,
0x10, 0x00,
0xfe, 0x00,
0x7c, 0x00,
0x38, 0x00,
0x10, 0x00,
0x00, 0x00,
};
const unsigned char rightcursor_bits[] = {
0x00, 0x00, 0x10, 0x00, 0x18, 0x00, 0x1c, 0x00, 0xfe, 0x00, 0x1c, 0x00,
0x18, 0x00, 0x10, 0x00, 0x00, 0x00
};
const unsigned char startMark_bits[] = {
0xc1, 0x80, /* ##-- ---# # */
0xe3, 0x80, /* ###- --## # */
0x77, 0x00, /* -### -### - */
0x3e, 0x00, /* --## ###- - */
0x1c, 0x00, /* ---# ##-- - */
0x3e, 0x00, /* --## ###- - */
0x77, 0x00, /* -### -### - */
0xe3, 0x80, /* ###- --## # */
0xc1, 0x80, /* ##-- ---# # */
};
#ifdef USE_PATTERNS
const unsigned char bonus_bits[][4] = {
{ 0x88, 0x44, 0x22, 0x11 },
{ 0xaa, 0x55, 0xaa, 0x55 },
{ 0xCC, 0x66, 0x33, 0x99 },
{ 0xCC, 0xCC, 0x33, 0x33 }
};
#endif
DrawCtx*
frank_drawctxt_make( MPFORMAL CWindow* window )
{
FrankDrawCtx* dctx = (FrankDrawCtx*)XP_MALLOC( mpool,
sizeof(FrankDrawCtx) );
U16 i;
dctx->vtable = (DrawCtxVTable*)
XP_MALLOC( mpool, sizeof(*(((FrankDrawCtx*)dctx)->vtable)) );
for ( i = 0; i < sizeof(*dctx->vtable)/4; ++i ) {
((void**)(dctx->vtable))[i] = draw_doNothing;
}
/* SET_VTABLE_ENTRY( dctx, draw_destroyCtxt, frank_ ); */
SET_VTABLE_ENTRY( dctx->vtable, draw_boardBegin, frank );
SET_VTABLE_ENTRY( dctx->vtable, draw_drawCell, frank );
SET_VTABLE_ENTRY( dctx->vtable, draw_invertCell, frank );
SET_VTABLE_ENTRY( dctx->vtable, draw_boardFinished, frank );
SET_VTABLE_ENTRY( dctx->vtable, draw_trayBegin, frank );
SET_VTABLE_ENTRY( dctx->vtable, draw_drawTile, frank );
SET_VTABLE_ENTRY( dctx->vtable, draw_drawTileBack, frank );
SET_VTABLE_ENTRY( dctx->vtable, draw_drawTrayDivider, frank );
SET_VTABLE_ENTRY( dctx->vtable, draw_clearRect, frank );
SET_VTABLE_ENTRY( dctx->vtable, draw_drawBoardArrow, frank );
SET_VTABLE_ENTRY( dctx->vtable, draw_scoreBegin, frank );
SET_VTABLE_ENTRY( dctx->vtable, draw_measureRemText, frank );
SET_VTABLE_ENTRY( dctx->vtable, draw_drawRemText, frank );
SET_VTABLE_ENTRY( dctx->vtable, draw_measureScoreText, frank );
SET_VTABLE_ENTRY( dctx->vtable, draw_score_drawPlayer, frank );
SET_VTABLE_ENTRY( dctx->vtable, draw_score_pendingScore, frank );
SET_VTABLE_ENTRY( dctx->vtable, draw_scoreFinished, frank );
SET_VTABLE_ENTRY( dctx->vtable, draw_drawTimer, frank );
SET_VTABLE_ENTRY( dctx->vtable, draw_getMiniWText, frank );
SET_VTABLE_ENTRY( dctx->vtable, draw_measureMiniWText, frank );
SET_VTABLE_ENTRY( dctx->vtable, draw_drawMiniWindow, frank );
SET_VTABLE_ENTRY( dctx->vtable, draw_eraseMiniWindow, frank );
dctx->window = window;
dctx->valFont = GUI_GetFont( 9, CTRL_NORMAL );
dctx->scoreFnt = GUI_GetFont( 12, CTRL_NORMAL );
dctx->scoreFntBold = GUI_GetFont( 12, CTRL_BOLD );
dctx->trayFont = GUI_GetFont( 16, CTRL_NORMAL );
IMAGE downcursor = { 9, 9, 2,
COLOR_MODE_MONO, 0, (const COLOR *) 0,
(U8*)downcursor_bits };
XP_MEMCPY( (IMAGE*)&dctx->downcursor, &downcursor,
sizeof(dctx->downcursor) );
IMAGE rightcursor = { 9, 9, 2,
COLOR_MODE_MONO, 0, (const COLOR *) 0,
(U8*)rightcursor_bits };
XP_MEMCPY( (IMAGE*)&dctx->rightcursor, &rightcursor,
sizeof(dctx->rightcursor) );
IMAGE startMark = { 9, 9, 2,
COLOR_MODE_MONO, 0, (const COLOR *) 0,
(U8*)startMark_bits };
XP_MEMCPY( (IMAGE*)&dctx->startMark, &startMark,
sizeof(dctx->startMark) );
#ifdef USE_PATTERNS
for ( i = 0; i < BONUS_LAST; ++i ) {
IMAGE bonus = { 8, 4, 1,
COLOR_MODE_MONO, 0, (const COLOR *) 0,
(U8*)bonus_bits[i] };
XP_MEMCPY( (IMAGE*)&dctx->bonusImages[i], &bonus,
sizeof(dctx->bonusImages[i]) );
}
#endif
return (DrawCtx*)dctx;
} /* frank_drawctxt_make */