xwords/xwords4/linux/gtkdraw.c
ehouse bc05761c4a change API for drawing divider so it can be focussed as well as
selected.  Being using that information in palm and wince.  For GTK,
use color rather than ugly black rects to indicate focus.
2008-07-17 05:03:01 +00:00

1144 lines
35 KiB
C

/* -*- mode: C; fill-column: 78; c-basic-offset: 4; compile-command: "make MEMDEBUG=TRUE"; -*- */
/*
* Copyright 1997-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.
*/
#ifdef PLATFORM_GTK
#include <stdlib.h>
#include <stdio.h>
#include <gdk/gdkdrawable.h>
#include "gtkmain.h"
#include "draw.h"
#include "board.h"
#include "linuxmain.h"
typedef enum {
XP_GTK_JUST_NONE
,XP_GTK_JUST_CENTER
,XP_GTK_JUST_TOPLEFT
,XP_GTK_JUST_BOTTOMRIGHT
} XP_GTK_JUST;
typedef struct FontPerSize {
unsigned int ht;
PangoFontDescription* fontdesc;
PangoLayout* layout;
} FontPerSize;
/* static GdkGC* newGCForColor( GdkWindow* window, XP_Color* newC ); */
static void
insetRect( XP_Rect* r, short i )
{
r->top += i;
r->left += i;
i *= 2;
r->width -= i;
r->height -= i;
} /* insetRect */
#if 0
#define DRAW_WHAT(dc) ((dc)->globals->pixmap)
#else
#define DRAW_WHAT(dc) ((dc)->drawing_area->window)
#endif
#define GTKMIN_W_HT 12
static void
gtkEraseRect( GtkDrawCtx* dctx, const XP_Rect* rect )
{
gdk_draw_rectangle( DRAW_WHAT(dctx),
dctx->drawing_area->style->white_gc,
TRUE, rect->left, rect->top,
rect->width, rect->height );
} /* gtkEraseRect */
static void
frameRect( GtkDrawCtx* dctx, const XP_Rect* rect )
{
gdk_draw_rectangle( DRAW_WHAT(dctx),
dctx->drawGC, FALSE, rect->left, rect->top,
rect->width, rect->height );
} /* frameRect */
#ifdef DRAW_WITH_PRIMITIVES
static void
gtk_prim_draw_setClip( DrawCtx* p_dctx, XP_Rect* newClip, XP_Rect* oldClip)
{
} /* gtk_prim_draw_setClip */
static void
gtk_prim_draw_frameRect( DrawCtx* p_dctx, XP_Rect* rect )
{
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
frameRect( dctx, rect );
} /* gtk_prim_draw_frameRect */
static void
gtk_prim_draw_invertRect( DrawCtx* p_dctx, XP_Rect* rect )
{
/* not sure you can do this on GTK!! */
} /* gtk_prim_draw_invertRect */
static void
gtk_prim_draw_clearRect( DrawCtx* p_dctx, XP_Rect* rect )
{
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
gtkEraseRect( dctx, rect );
} /* gtk_prim_draw_clearRect */
static void
gtk_prim_draw_drawString( DrawCtx* p_dctx, XP_UCHAR* str,
XP_U16 x, XP_U16 y )
{
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
XP_U16 fontHeight = 10; /* FIX ME */
gdk_draw_string( DRAW_WHAT(dctx), dctx->gdkFont, dctx->drawGC,
x, y + fontHeight, str );
} /* gtk_prim_draw_drawString */
static void
gtk_prim_draw_drawBitmap( DrawCtx* p_dctx, XP_Bitmap bm,
XP_U16 x, XP_U16 y )
{
} /* gtk_prim_draw_drawBitmap */
static void
gtk_prim_draw_measureText( DrawCtx* p_dctx, XP_UCHAR* str,
XP_U16* widthP, XP_U16* heightP )
{
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
gint len = strlen(str);
gint width = gdk_text_measure( dctx->gdkFont, str, len );
*widthP = width;
*heightP = 12; /* ??? :-) */
} /* gtk_prim_draw_measureText */
#endif /* DRAW_WITH_PRIMITIVES */
static gint
compForHt( gconstpointer a,
gconstpointer b )
{
FontPerSize* fps1 = (FontPerSize*)a;
FontPerSize* fps2 = (FontPerSize*)b;
return fps1->ht - fps2->ht;
}
static PangoLayout*
layout_for_ht( GtkDrawCtx* dctx, XP_U16 ht )
{
PangoLayout* layout = NULL;
/* Try to find a cached layout. Otherwise create a new one. */
FontPerSize fps = { .ht = ht };
GList* gl = g_list_find_custom( dctx->fontsPerSize, &fps,
compForHt );
if ( NULL != gl ) {
layout = ((FontPerSize*)gl->data)->layout;
}
if ( NULL == layout ) {
FontPerSize* fps = g_malloc( sizeof(*fps) );
dctx->fontsPerSize = g_list_insert( dctx->fontsPerSize,
fps, 0 );
char font[32];
snprintf( font, sizeof(font), "helvetica normal %dpx", ht );
layout = pango_layout_new( dctx->pangoContext );
fps->fontdesc = pango_font_description_from_string( font );
pango_layout_set_font_description( layout, fps->fontdesc );
fps->layout = layout;
/* This only happens first time??? */
pango_layout_set_alignment( layout, PANGO_ALIGN_CENTER );
fps->ht = ht;
}
return layout;
} /* layout_for_ht */
static void
draw_string_at( GtkDrawCtx* dctx, const char* str, XP_U16 fontHt,
const XP_Rect* where, XP_GTK_JUST just,
const GdkColor* frground, const GdkColor* bkgrnd )
{
PangoLayout* layout;
gint xx = where->left;
gint yy = where->top;
layout = layout_for_ht( dctx, fontHt );
pango_layout_set_text( layout, str, strlen(str) );
if ( just != XP_GTK_JUST_NONE ) {
int width, height;
pango_layout_get_pixel_size( layout, &width, &height );
switch( just ) {
case XP_GTK_JUST_CENTER:
xx += (where->width - width) / 2;
yy += (where->height - height) / 2;
break;
case XP_GTK_JUST_BOTTOMRIGHT:
xx += where->width - width;
yy += where->height - height;
break;
case XP_GTK_JUST_TOPLEFT:
default:
/* nothing to do?? */
break;
}
}
gdk_draw_layout_with_colors( DRAW_WHAT(dctx), dctx->drawGC,
xx, yy, layout,
frground, bkgrnd );
} /* draw_string_at */
static void
drawBitmapFromLBS( GtkDrawCtx* dctx, XP_Bitmap bm, const XP_Rect* rect )
{
GdkPixmap* pm;
LinuxBMStruct* lbs = (LinuxBMStruct*)bm;
gint x, y;
XP_U8* bp;
XP_U16 i;
XP_S16 nBytes;
XP_U16 nCols, nRows;
nCols = lbs->nCols;
nRows = lbs->nRows;
bp = (XP_U8*)(lbs + 1); /* point to the bitmap data */
nBytes = lbs->nBytes;
pm = gdk_pixmap_new( DRAW_WHAT(dctx), nCols, nRows, -1 );
gdk_draw_rectangle( pm, dctx->drawing_area->style->white_gc, TRUE,
0, 0, nCols, nRows );
x = 0;
y = 0;
while ( nBytes-- ) {
XP_U8 byte = *bp++;
for ( i = 0; i < 8; ++i ) {
XP_Bool draw = ((byte & 0x80) != 0);
if ( draw ) {
gdk_draw_point( pm, dctx->drawing_area->style->black_gc, x, y );
}
byte <<= 1;
if ( ++x == nCols ) {
x = 0;
if ( ++y == nRows ) {
break;
}
}
}
}
XP_ASSERT( nBytes == -1 ); /* else we're out of sync */
gdk_draw_drawable( DRAW_WHAT(dctx),
dctx->drawGC,
(GdkDrawable*)pm, 0, 0,
rect->left+2,
rect->top+2,
lbs->nCols,
lbs->nRows );
g_object_unref( pm );
} /* drawBitmapFromLBS */
static void
freer( gpointer data, gpointer XP_UNUSED(user_data) )
{
FontPerSize* fps = (FontPerSize*)data;
pango_font_description_free( fps->fontdesc );
g_object_unref( fps->layout );
g_free( fps );
}
static void
gtk_draw_destroyCtxt( DrawCtx* p_dctx )
{
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
GtkAllocation* alloc = &dctx->drawing_area->allocation;
gdk_draw_rectangle( DRAW_WHAT(dctx),
dctx->drawing_area->style->white_gc,
TRUE,
0, 0, alloc->width, alloc->height );
g_list_foreach( dctx->fontsPerSize, freer, NULL );
g_list_free( dctx->fontsPerSize );
g_object_unref( dctx->pangoContext );
} /* gtk_draw_destroyCtxt */
static XP_Bool
gtk_draw_boardBegin( DrawCtx* p_dctx, const DictionaryCtxt* XP_UNUSED(dict),
const XP_Rect* rect, DrawFocusState XP_UNUSED(dfs) )
{
GdkRectangle gdkrect;
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
gdk_gc_set_foreground( dctx->drawGC, &dctx->black );
gdkrect = *(GdkRectangle*)rect;
++gdkrect.width;
++gdkrect.height;
/* gdk_gc_set_clip_rectangle( dctx->drawGC, &gdkrect ); */
return XP_TRUE;
} /* draw_finish */
static void
gtk_draw_objFinished( DrawCtx* XP_UNUSED(p_dctx),
BoardObjectType XP_UNUSED(typ),
const XP_Rect* XP_UNUSED(rect),
DrawFocusState XP_UNUSED(dfs) )
{
} /* draw_finished */
static XP_Bool
gtk_draw_vertScrollBoard( DrawCtx* p_dctx, XP_Rect* rect,
XP_S16 dist, DrawFocusState XP_UNUSED(dfs) )
{
/* Turn this on to mimic what palm does, but need to figure out some gtk
analog to copybits for it to actually work. */
#if 1
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
XP_Bool down = dist <= 0;
gint ysrc, ydest;
if ( down ) {
ysrc = rect->top;
dist = -dist; /* make it positive */
ydest = ysrc + dist;
} else {
ydest = rect->top;
ysrc = ydest + dist;
}
gdk_draw_drawable( DRAW_WHAT(dctx),
dctx->drawGC,
DRAW_WHAT(dctx),
rect->left,
ysrc,
rect->left,
ydest,
rect->width,
rect->height - dist );
if ( !down ) {
rect->top += rect->height - dist;
}
rect->height = dist;
#endif
return XP_TRUE;
}
static void
drawHintBorders( GtkDrawCtx* dctx, const XP_Rect* rect, HintAtts hintAtts)
{
if ( hintAtts != HINT_BORDER_NONE && hintAtts != HINT_BORDER_CENTER ) {
XP_Rect lrect = *rect;
insetRect( &lrect, 1 );
gdk_gc_set_foreground( dctx->drawGC, &dctx->black );
if ( (hintAtts & HINT_BORDER_LEFT) != 0 ) {
gdk_draw_rectangle( DRAW_WHAT(dctx),
dctx->drawGC,
FALSE, lrect.left, lrect.top,
0, lrect.height);
}
if ( (hintAtts & HINT_BORDER_TOP) != 0 ) {
gdk_draw_rectangle( DRAW_WHAT(dctx),
dctx->drawGC,
FALSE, lrect.left, lrect.top,
lrect.width, 0/*rectInset.height*/);
}
if ( (hintAtts & HINT_BORDER_RIGHT) != 0 ) {
gdk_draw_rectangle( DRAW_WHAT(dctx),
dctx->drawGC,
FALSE, lrect.left+lrect.width,
lrect.top,
0, lrect.height);
}
if ( (hintAtts & HINT_BORDER_BOTTOM) != 0 ) {
gdk_draw_rectangle( DRAW_WHAT(dctx),
dctx->drawGC,
FALSE, lrect.left,
lrect.top+lrect.height,
lrect.width, 0 );
}
}
}
static XP_Bool
gtk_draw_drawCell( DrawCtx* p_dctx, const XP_Rect* rect, const XP_UCHAR* letter,
XP_Bitmap bitmap, Tile XP_UNUSED(tile), XP_S16 owner,
XWBonusType bonus, HintAtts hintAtts, CellFlags flags )
{
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
XP_Rect rectInset = *rect;
XP_Bool showGrid = dctx->globals->gridOn;
XP_Bool highlight = (flags & CELL_HIGHLIGHT) != 0;
GdkColor* cursor =
((flags & CELL_ISCURSOR) != 0) ? &dctx->cursor : NULL;
gtkEraseRect( dctx, rect );
insetRect( &rectInset, 1 );
if ( showGrid ) {
gdk_gc_set_foreground( dctx->drawGC, &dctx->black );
gdk_draw_rectangle( DRAW_WHAT(dctx),
dctx->drawGC,
FALSE,
rect->left, rect->top, rect->width,
rect->height );
}
/* We draw just an empty, potentially colored, square IFF there's nothing
in the cell or if CELL_DRAGSRC is set */
if ( (flags & (CELL_DRAGSRC|CELL_ISEMPTY)) != 0 ) {
if ( !!cursor || bonus != BONUS_NONE ) {
GdkColor* foreground;
if ( !!cursor ) {
foreground = cursor;
} else if ( bonus != BONUS_NONE ) {
foreground = &dctx->bonusColors[bonus-1];
} else {
foreground = NULL;
}
if ( !!foreground ) {
gdk_gc_set_foreground( dctx->drawGC, foreground );
gdk_draw_rectangle( DRAW_WHAT(dctx), dctx->drawGC, TRUE,
rectInset.left, rectInset.top,
rectInset.width+1, rectInset.height+1 );
}
}
if ( (flags & CELL_ISSTAR) != 0 ) {
draw_string_at( dctx, "*", rect->height, rect, XP_GTK_JUST_CENTER,
&dctx->black, NULL );
}
} else if ( !!letter ) {
GdkColor* foreground = cursor;
if ( cursor ) {
gdk_gc_set_foreground( dctx->drawGC, cursor );
} else if ( !highlight ) {
gdk_gc_set_foreground( dctx->drawGC, &dctx->tileBack );
}
gdk_draw_rectangle( DRAW_WHAT(dctx), dctx->drawGC, TRUE,
rectInset.left, rectInset.top,
rectInset.width+1, rectInset.height+1 );
if ( !foreground ) {
foreground = highlight? &dctx->white : &dctx->playerColors[owner];
}
draw_string_at( dctx, letter, rectInset.height-2, &rectInset,
XP_GTK_JUST_CENTER, foreground, NULL );
if ( (flags & CELL_ISBLANK) != 0 ) {
gdk_draw_arc( DRAW_WHAT(dctx), dctx->drawGC,
0, /* filled */
rect->left, /* x */
rect->top, /* y */
rect->width,/*width, */
rect->height,/*width, */
0, 360*64 );
}
} else if ( !!bitmap ) {
drawBitmapFromLBS( dctx, bitmap, rect );
}
drawHintBorders( dctx, rect, hintAtts );
return XP_TRUE;
} /* gtk_draw_drawCell */
static void
gtk_draw_invertCell( DrawCtx* XP_UNUSED(p_dctx),
const XP_Rect* XP_UNUSED(rect) )
{
/* GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx; */
/* (void)gtk_draw_drawMiniWindow( p_dctx, "f", rect); */
/* GdkGCValues values; */
/* gdk_gc_get_values( dctx->drawGC, &values ); */
/* gdk_gc_set_function( dctx->drawGC, GDK_INVERT ); */
/* gdk_gc_set_clip_rectangle( dctx->drawGC, (GdkRectangle*)rect ); */
/* gdk_draw_rectangle( DRAW_WHAT(dctx), dctx->drawGC, */
/* TRUE, rect->left, rect->top, */
/* rect->width, rect->height ); */
/* gdk_gc_set_function( dctx->drawGC, values.function ); */
} /* gtk_draw_invertCell */
static XP_Bool
gtk_draw_trayBegin( DrawCtx* p_dctx, const XP_Rect* XP_UNUSED(rect),
XP_U16 owner, DrawFocusState dfs )
{
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
dctx->trayOwner = owner;
dctx->topFocus = dfs == DFS_TOP;
return XP_TRUE;
} /* gtk_draw_trayBegin */
static void
gtkDrawTileImpl( DrawCtx* p_dctx, const XP_Rect* rect, const XP_UCHAR* textP,
XP_Bitmap bitmap, XP_S16 val, CellFlags flags,
XP_Bool clearBack )
{
XP_UCHAR numbuf[3];
gint len;
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
XP_Rect insetR = *rect;
XP_Bool isCursor = (flags & CELL_ISCURSOR) != 0;
if ( clearBack ) {
gtkEraseRect( dctx, &insetR );
}
if ( isCursor || (val >= 0) ) {
GdkColor* foreground = &dctx->playerColors[dctx->trayOwner];
XP_Rect formatRect = insetR;
insetRect( &insetR, 1 );
if ( clearBack ) {
gdk_gc_set_foreground( dctx->drawGC,
isCursor? &dctx->cursor:&dctx->tileBack );
gdk_draw_rectangle( DRAW_WHAT(dctx),
dctx->drawGC,
XP_TRUE,
insetR.left, insetR.top, insetR.width,
insetR.height );
}
if ( val >= 0 ) {
formatRect.left += 3;
formatRect.width -= 6;
if ( !!textP ) {
if ( *textP != LETTER_NONE ) { /* blank */
draw_string_at( dctx, textP, formatRect.height>>1,
&formatRect, XP_GTK_JUST_TOPLEFT,
foreground, NULL );
}
} else if ( !!bitmap ) {
drawBitmapFromLBS( dctx, bitmap, &insetR );
}
sprintf( numbuf, "%d", val );
len = strlen( numbuf );
draw_string_at( dctx, numbuf, formatRect.height>>2,
&formatRect, XP_GTK_JUST_BOTTOMRIGHT,
foreground, NULL );
/* frame the tile */
gdk_gc_set_foreground( dctx->drawGC, &dctx->black );
gdk_draw_rectangle( DRAW_WHAT(dctx),
dctx->drawGC,
FALSE,
insetR.left, insetR.top, insetR.width,
insetR.height );
if ( (flags & CELL_HIGHLIGHT) != 0 ) {
insetRect( &insetR, 1 );
gdk_draw_rectangle( DRAW_WHAT(dctx),
dctx->drawGC,
FALSE, insetR.left, insetR.top,
insetR.width, insetR.height);
}
}
}
} /* gtkDrawTileImpl */
static void
gtk_draw_drawTile( DrawCtx* p_dctx, const XP_Rect* rect, const XP_UCHAR* textP,
XP_Bitmap bitmap, XP_S16 val, CellFlags flags )
{
gtkDrawTileImpl( p_dctx, rect, textP, bitmap, val, flags, XP_TRUE );
}
#ifdef POINTER_SUPPORT
static void
gtk_draw_drawTileMidDrag( DrawCtx* p_dctx, const XP_Rect* rect,
const XP_UCHAR* textP, XP_Bitmap bitmap,
XP_S16 val, XP_U16 owner, CellFlags flags )
{
gtk_draw_trayBegin( p_dctx, rect, owner, DFS_NONE );
gtkDrawTileImpl( p_dctx, rect, textP, bitmap, val,
flags | CELL_HIGHLIGHT,
XP_FALSE );
}
#endif
static void
gtk_draw_drawTileBack( DrawCtx* p_dctx, const XP_Rect* rect,
CellFlags flags )
{
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
XP_Bool hasCursor = (flags & CELL_ISCURSOR) != 0;
XP_Rect r = *rect;
insetRect( &r, 1 );
gdk_gc_set_foreground( dctx->drawGC,
&dctx->playerColors[dctx->trayOwner] );
gdk_draw_rectangle( DRAW_WHAT(dctx),
dctx->drawGC, TRUE,
r.left, r.top, r.width, r.height );
insetRect( &r, 1 );
gdk_gc_set_foreground( dctx->drawGC,
hasCursor? &dctx->cursor : &dctx->tileBack );
gdk_draw_rectangle( DRAW_WHAT(dctx),
dctx->drawGC, TRUE,
r.left, r.top, r.width, r.height );
draw_string_at( dctx, "?", r.height,
&r, XP_GTK_JUST_CENTER,
&dctx->playerColors[dctx->trayOwner], NULL );
} /* gtk_draw_drawTileBack */
static void
gtk_draw_drawTrayDivider( DrawCtx* p_dctx, const XP_Rect* rect,
CellFlags flags )
{
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
XP_Rect r = *rect;
XP_Bool selected = 0 != (flags & CELL_HIGHLIGHT);
XP_Bool isCursor = 0 != (flags & CELL_ISCURSOR);
gtkEraseRect( dctx, &r );
gdk_gc_set_foreground( dctx->drawGC,
isCursor? &dctx->cursor:&dctx->white );
gdk_draw_rectangle( DRAW_WHAT(dctx),
dctx->drawGC, XP_TRUE,
r.left, r.top, r.width, r.height );
r.left += 2;
r.width -= 4;
if ( selected ) {
--r.height;
}
gdk_gc_set_foreground( dctx->drawGC, &dctx->black );
gdk_draw_rectangle( DRAW_WHAT(dctx),
dctx->drawGC,
!selected,
r.left, r.top, r.width, r.height);
} /* gtk_draw_drawTrayDivider */
#if 0
static void
gtk_draw_frameBoard( DrawCtx* p_dctx, XP_Rect* rect )
{
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
gdk_gc_set_foreground( dctx->drawGC, &dctx->black );
gdk_draw_rectangle( DRAW_WHAT(dctx),
dctx->drawGC, FALSE,
rect->left, rect->top, rect->width, rect->height );
} /* gtk_draw_frameBoard */
static void
gtk_draw_frameTray( DrawCtx* p_dctx, const XP_Rect* rect )
{
// GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
} /* gtk_draw_frameBoard */
#endif
static void
gtk_draw_clearRect( DrawCtx* p_dctx, const XP_Rect* rectP )
{
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
XP_Rect rect = *rectP;
++rect.width;
++rect.top;
gtkEraseRect( dctx, &rect );
} /* gtk_draw_clearRect */
static void
gtk_draw_drawBoardArrow( DrawCtx* p_dctx, const XP_Rect* rectP,
XWBonusType XP_UNUSED(cursorBonus), XP_Bool vertical,
HintAtts hintAtts, CellFlags XP_UNUSED(flags) )
{
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
const char* curs = vertical? "|":"-";
/* font needs to be small enough that "|" doesn't overwrite cell below */
draw_string_at( dctx, curs, (rectP->height*2)/3,
rectP, XP_GTK_JUST_CENTER,
&dctx->black, NULL );
drawHintBorders( dctx, rectP, hintAtts );
} /* gtk_draw_drawBoardCursor */
static void
gtk_draw_scoreBegin( DrawCtx* p_dctx, const XP_Rect* rect,
XP_U16 XP_UNUSED(numPlayers),
DrawFocusState dfs )
{
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
/* gdk_gc_set_clip_rectangle( dctx->drawGC, (GdkRectangle*)rect ); */
gtkEraseRect( dctx, rect );
dctx->topFocus = dfs == DFS_TOP;
} /* gtk_draw_scoreBegin */
static PangoLayout*
getLayoutToFitRect( GtkDrawCtx* dctx, const char* str, const XP_Rect* rect )
{
PangoLayout* layout;
float ratio, ratioH;
int width, height;
XP_U16 len = strlen(str);
/* First measure it using any font at all */
layout = layout_for_ht( dctx, 24 );
pango_layout_set_text( layout, str, len );
pango_layout_get_pixel_size( layout, &width, &height );
/* Figure the ratio of is to should-be. The smaller of these is the one
we must use. */
ratio = (float)rect->width / (float)width;
ratioH = (float)rect->height / (float)height;
if ( ratioH < ratio ) {
ratio = ratioH;
}
height = 24.0 * ratio;
layout = layout_for_ht( dctx, height );
pango_layout_set_text( layout, str, len );
return layout;
} /* getLayoutToFitRect */
static void
gtkDrawDrawRemText( DrawCtx* p_dctx, const XP_Rect* rect, XP_S16 nTilesLeft,
XP_U16* widthP, XP_U16* heightP )
{
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
char buf[10];
snprintf( buf, sizeof(buf), "rem:%d", nTilesLeft );
if ( !!widthP ) {
int width, height;
PangoLayout* layout = getLayoutToFitRect( dctx, buf, rect );
pango_layout_get_pixel_size( layout, &width, &height );
if ( width > rect->width ) {
width = rect->width;
}
if ( height > rect->height ) {
height = rect->height;
}
*widthP = width;
*heightP = height;
} else {
draw_string_at( dctx, buf, rect->height,
rect, XP_GTK_JUST_TOPLEFT,
&dctx->black, NULL );
}
} /* gtkDrawDrawRemText */
static void
gtk_draw_measureRemText( DrawCtx* p_dctx, const XP_Rect* r, XP_S16 nTilesLeft,
XP_U16* width, XP_U16* height )
{
gtkDrawDrawRemText( p_dctx, r, nTilesLeft, width, height );
} /* gtk_draw_measureRemText */
static void
gtk_draw_drawRemText( DrawCtx* p_dctx, const XP_Rect* rInner,
const XP_Rect* XP_UNUSED(rOuter), XP_S16 nTilesLeft )
{
gtkDrawDrawRemText( p_dctx, rInner, nTilesLeft, NULL, NULL );
} /* gtk_draw_drawRemText */
static void
formatScoreText( char* buf, XP_U16 bufLen, const DrawScoreInfo* dsi )
{
XP_S16 score = dsi->totalScore;
XP_U16 nTilesLeft = dsi->nTilesLeft;
XP_Bool isTurn = dsi->isTurn;
int used;
char* borders = "";
if ( isTurn ) {
borders = "*";
}
used = snprintf( buf, bufLen, "%s%.3d", borders, score );
if ( (nTilesLeft < MAX_TRAY_TILES) && (nTilesLeft > 0) ) {
char nbuf[10];
sprintf( nbuf, ":%d", nTilesLeft );
(void)strcat( buf, nbuf );
}
snprintf( buf+used, bufLen-used, "%s", borders );
} /* formatScoreText */
static void
gtk_draw_measureScoreText( DrawCtx* p_dctx, const XP_Rect* bounds,
const DrawScoreInfo* dsi,
XP_U16* widthP, XP_U16* heightP )
{
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
char buf[20];
PangoLayout* layout;
int height, width;
formatScoreText( buf, sizeof(buf), dsi );
layout = getLayoutToFitRect( dctx, buf, bounds );
pango_layout_get_pixel_size( layout, &width, &height );
*widthP = width;
*heightP = height;
} /* gtk_draw_measureScoreText */
static void
gtk_draw_score_drawPlayer( DrawCtx* p_dctx, const XP_Rect* rInner,
const XP_Rect* rOuter, const DrawScoreInfo* dsi )
{
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
char scoreBuf[20];
XP_Bool hasCursor = (dsi->flags & CELL_ISCURSOR) != 0;
GdkColor* cursor = NULL;
formatScoreText( scoreBuf, sizeof(scoreBuf), dsi );
if ( hasCursor ) {
cursor = &dctx->cursor;
gdk_gc_set_foreground( dctx->drawGC, cursor );
gdk_draw_rectangle( DRAW_WHAT(dctx), dctx->drawGC,
TRUE, rOuter->left, rOuter->top,
rOuter->width, rOuter->height );
}
gdk_gc_set_foreground( dctx->drawGC, &dctx->playerColors[dsi->playerNum] );
if ( dsi->selected ) {
gdk_draw_rectangle( DRAW_WHAT(dctx), dctx->drawGC,
TRUE, rOuter->left, rOuter->top,
rOuter->width, rOuter->height );
gtkEraseRect( dctx, rInner );
}
draw_string_at( dctx, scoreBuf, rInner->height - 1,
rInner, XP_GTK_JUST_CENTER,
&dctx->playerColors[dsi->playerNum], cursor );
} /* gtk_draw_score_drawPlayer */
static void
gtk_draw_score_pendingScore( DrawCtx* p_dctx, const XP_Rect* rect,
XP_S16 score, XP_U16 XP_UNUSED(playerNum),
CellFlags flags )
{
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
char buf[5];
XP_U16 ht;
XP_Rect localR;
GdkColor* cursor = ((flags & CELL_ISCURSOR) != 0)
? &dctx->cursor : NULL;
if ( score >= 0 ) {
sprintf( buf, "%.3d", score );
} else {
strcpy( buf, "???" );
}
/* gdk_gc_set_clip_rectangle( dctx->drawGC, (GdkRectangle*)rect ); */
localR = *rect;
insetRect( &localR, 1 );
gtkEraseRect( dctx, &localR );
ht = localR.height >> 2;
draw_string_at( dctx, "Pts:", ht,
&localR, XP_GTK_JUST_TOPLEFT,
&dctx->black, cursor );
draw_string_at( dctx, buf, ht,
&localR, XP_GTK_JUST_BOTTOMRIGHT,
&dctx->black, cursor );
} /* gtk_draw_score_pendingScore */
static void
gtkFormatTimerText( XP_UCHAR* buf, XP_S16 secondsLeft )
{
XP_U16 minutes, seconds;
if ( secondsLeft < 0 ) {
*buf++ = '-';
secondsLeft *= -1;
}
minutes = secondsLeft / 60;
seconds = secondsLeft % 60;
sprintf( buf, "% 1d:%02d", minutes, seconds );
} /* gtkFormatTimerText */
static void
gtk_draw_drawTimer( DrawCtx* p_dctx, const XP_Rect* rInner,
const XP_Rect* XP_UNUSED(rOuter),
XP_U16 XP_UNUSED(player), XP_S16 secondsLeft )
{
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
XP_UCHAR buf[10];
gtkFormatTimerText( buf, secondsLeft );
/* gdk_gc_set_clip_rectangle( dctx->drawGC, (GdkRectangle*)rInner ); */
gtkEraseRect( dctx, rInner );
draw_string_at( dctx, buf, rInner->height-1,
rInner, XP_GTK_JUST_CENTER,
&dctx->black, NULL );
} /* gtk_draw_drawTimer */
#define MINI_LINE_HT 12
#define MINI_V_PADDING 6
#define MINI_H_PADDING 8
static const XP_UCHAR*
gtk_draw_getMiniWText( DrawCtx* XP_UNUSED(p_dctx), XWMiniTextType textHint )
{
/* GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx; */
XP_UCHAR* str;
switch( textHint ) {
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 = "Trading tiles;\nclick D when done"; break;
default:
XP_ASSERT( XP_FALSE );
}
return str;
} /* gtk_draw_getMiniWText */
static void
gtk_draw_measureMiniWText( DrawCtx* p_dctx, const XP_UCHAR* str,
XP_U16* widthP, XP_U16* heightP )
{
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
int height, width;
PangoLayout* layout = layout_for_ht( dctx, GTKMIN_W_HT );
pango_layout_set_text( layout, str, strlen(str) );
pango_layout_get_pixel_size( layout, &width, &height );
*heightP = height;
*widthP = width + 6;
} /* gtk_draw_measureMiniWText */
static void
gtk_draw_drawMiniWindow( DrawCtx* p_dctx, const XP_UCHAR* text,
const XP_Rect* rect, void** XP_UNUSED(closureP) )
{
GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx;
XP_Rect localR = *rect;
gdk_gc_set_foreground( dctx->drawGC, &dctx->black );
/* gdk_gc_set_clip_rectangle( dctx->drawGC, (GdkRectangle*)&localR ); */
/* play some skanky games to get the shadow drawn under and to the
right... */
gtkEraseRect( dctx, &localR );
insetRect( &localR, 1 );
--localR.width;
--localR.height;
frameRect( dctx, &localR );
--localR.top;
--localR.left;
gtkEraseRect( dctx, &localR );
frameRect( dctx, &localR );
draw_string_at( dctx, text, GTKMIN_W_HT,
&localR, XP_GTK_JUST_CENTER,
&dctx->black, NULL );
} /* gtk_draw_drawMiniWindow */
#define SET_GDK_COLOR( c, r, g, b ) { \
c.red = (r); \
c.green = (g); \
c.blue = (b); \
}
static void
draw_doNothing( DrawCtx* XP_UNUSED(dctx), ... )
{
} /* draw_doNothing */
static void
allocAndSet( GdkColormap* map, GdkColor* color, unsigned short red,
unsigned short green, unsigned short blue )
{
gboolean success;
color->red = red;
color->green = green;
color->blue = blue;
success = gdk_colormap_alloc_color( map,
color,
TRUE, /* writeable */
TRUE ); /* best-match */
XP_ASSERT( success );
} /* allocAndSet */
DrawCtx*
gtkDrawCtxtMake( GtkWidget* drawing_area, GtkAppGlobals* globals )
{
GtkDrawCtx* dctx = g_malloc0( sizeof(GtkDrawCtx) );
GdkColormap* map;
short i;
dctx->vtable = g_malloc( sizeof(*(((GtkDrawCtx*)dctx)->vtable)) );
for ( i = 0; i < sizeof(*dctx->vtable)/4; ++i ) {
((void**)(dctx->vtable))[i] = draw_doNothing;
}
SET_VTABLE_ENTRY( dctx->vtable, draw_clearRect, gtk );
#ifdef DRAW_WITH_PRIMITIVES
SET_VTABLE_ENTRY( dctx->vtable, draw_setClip, gtk_prim );
SET_VTABLE_ENTRY( dctx->vtable, draw_frameRect, gtk_prim );
SET_VTABLE_ENTRY( dctx->vtable, draw_invertRect, gtk_prim );
SET_VTABLE_ENTRY( dctx->vtable, draw_drawString, gtk_prim );
SET_VTABLE_ENTRY( dctx->vtable, draw_drawBitmap, gtk_prim );
SET_VTABLE_ENTRY( dctx->vtable, draw_measureText, gtk_prim );
InitDrawDefaults( dctx->vtable );
#else
SET_VTABLE_ENTRY( dctx->vtable, draw_boardBegin, gtk );
SET_VTABLE_ENTRY( dctx->vtable, draw_drawCell, gtk );
SET_VTABLE_ENTRY( dctx->vtable, draw_invertCell, gtk );
SET_VTABLE_ENTRY( dctx->vtable, draw_objFinished, gtk );
SET_VTABLE_ENTRY( dctx->vtable, draw_vertScrollBoard, gtk );
SET_VTABLE_ENTRY( dctx->vtable, draw_trayBegin, gtk );
SET_VTABLE_ENTRY( dctx->vtable, draw_drawTile, gtk );
SET_VTABLE_ENTRY( dctx->vtable, draw_drawTileBack, gtk );
#ifdef POINTER_SUPPORT
SET_VTABLE_ENTRY( dctx->vtable, draw_drawTileMidDrag, gtk );
#endif
SET_VTABLE_ENTRY( dctx->vtable, draw_drawTrayDivider, gtk );
SET_VTABLE_ENTRY( dctx->vtable, draw_drawBoardArrow, gtk );
SET_VTABLE_ENTRY( dctx->vtable, draw_scoreBegin, gtk );
SET_VTABLE_ENTRY( dctx->vtable, draw_measureRemText, gtk );
SET_VTABLE_ENTRY( dctx->vtable, draw_drawRemText, gtk );
SET_VTABLE_ENTRY( dctx->vtable, draw_measureScoreText, gtk );
SET_VTABLE_ENTRY( dctx->vtable, draw_score_drawPlayer, gtk );
SET_VTABLE_ENTRY( dctx->vtable, draw_score_pendingScore, gtk );
SET_VTABLE_ENTRY( dctx->vtable, draw_drawTimer, gtk );
SET_VTABLE_ENTRY( dctx->vtable, draw_getMiniWText, gtk );
SET_VTABLE_ENTRY( dctx->vtable, draw_measureMiniWText, gtk );
SET_VTABLE_ENTRY( dctx->vtable, draw_drawMiniWindow, gtk );
SET_VTABLE_ENTRY( dctx->vtable, draw_destroyCtxt, gtk );
#endif
/* SET_VTABLE_ENTRY( dctx, draw_frameBoard, gtk_ ); */
/* SET_VTABLE_ENTRY( dctx, draw_frameTray, gtk_ ); */
dctx->pangoContext = gtk_widget_get_pango_context( drawing_area );
dctx->drawing_area = drawing_area;
dctx->globals = globals;
map = gdk_colormap_get_system();
allocAndSet( map, &dctx->black, 0x0000, 0x0000, 0x0000 );
allocAndSet( map, &dctx->white, 0xFFFF, 0xFFFF, 0xFFFF );
{
GdkWindow *window = NULL;
if ( GTK_WIDGET_FLAGS(GTK_WIDGET(drawing_area)) & GTK_NO_WINDOW ) {
/* XXX I'm not sure about this function because I never used it.
* (the name seems to indicate what you want though).
*/
window = gtk_widget_get_parent_window( GTK_WIDGET(drawing_area) );
} else {
window = GTK_WIDGET(drawing_area)->window;
}
dctx->drawGC = gdk_gc_new( window );
}
map = gdk_colormap_get_system();
allocAndSet( map, &dctx->black, 0x0000, 0x0000, 0x0000 );
allocAndSet( map, &dctx->white, 0xFFFF, 0xFFFF, 0xFFFF );
allocAndSet( map, &dctx->bonusColors[0], 0xFFFF, 0xAFFF, 0xAFFF );
allocAndSet( map, &dctx->bonusColors[1], 0xAFFF, 0xFFFF, 0xAFFF );
allocAndSet( map, &dctx->bonusColors[2], 0xAFFF, 0xAFFF, 0xFFFF );
allocAndSet( map, &dctx->bonusColors[3], 0xFFFF, 0xAFFF, 0xFFFF );
allocAndSet( map, &dctx->playerColors[0], 0x0000, 0x0000, 0xAFFF );
allocAndSet( map, &dctx->playerColors[1], 0xAFFF, 0x0000, 0x0000 );
allocAndSet( map, &dctx->playerColors[2], 0x0000, 0xAFFF, 0x0000 );
allocAndSet( map, &dctx->playerColors[3], 0xAFFF, 0x0000, 0xAFFF );
allocAndSet( map, &dctx->tileBack, 0xFFFF, 0xFFFF, 0x9999 );
allocAndSet( map, &dctx->cursor, 0x7FFF, 0x7FFF, 0xFFFF );
allocAndSet( map, &dctx->red, 0xFFFF, 0x0000, 0x0000 );
return (DrawCtx*)dctx;
} /* gtkDrawCtxtMake */
#endif /* PLATFORM_GTK */