/* -*- mode: C; fill-column: 78; c-basic-offset: 4; compile-command: "make MEMDEBUG=TRUE"; -*- */ /* * Copyright 1997-2007 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 #include #include #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 eraseRect( 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 ); } /* eraseRect */ static void frameRect( GtkDrawCtx* dctx, 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; eraseRect( 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 %d", 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 x = where->left; gint y = 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: x += (where->width - width) / 2; y += (where->height - height) / 2; break; case XP_GTK_JUST_BOTTOMRIGHT: x += where->width - width; y += where->height - height; break; case XP_GTK_JUST_TOPLEFT: default: /* nothing to do?? */ break; } } gdk_draw_layout_with_colors( DRAW_WHAT(dctx), dctx->drawGC, x, y, 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 drawFocusFrame( GtkDrawCtx* dctx, const XP_Rect* r ) { XP_Rect rectInset = *r; XP_U16 i; XP_U16 targetDim; targetDim = XP_MIN( rectInset.width, rectInset.height ); targetDim >>= 1; gdk_gc_set_foreground( dctx->drawGC, &dctx->black ); for ( i = 0; i < 5; ++i ) { insetRect( &rectInset, 1 ); if ( rectInset.width < targetDim || rectInset.height < targetDim ) { break; } gdk_draw_rectangle( DRAW_WHAT(dctx), dctx->drawGC, FALSE, rectInset.left, rectInset.top, rectInset.width+1, rectInset.height+1 ); } } 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* XP_UNUSED(p_dctx), XP_Rect* XP_UNUSED(rect), XP_S16 XP_UNUSED(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 0 XP_Bool up = dist < 0; if ( up ) { dist *= -1; } else { 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; eraseRect( dctx, rect ); insetRect( &rectInset, 1 ); gdk_gc_set_foreground( dctx->drawGC, &dctx->black ); if ( showGrid ) { gdk_draw_rectangle( DRAW_WHAT(dctx), dctx->drawGC, FALSE, rect->left, rect->top, rect->width, rect->height ); } /* draw the bonus colors only if we're not putting a "tile" there */ if ( !!letter ) { if ( *letter == LETTER_NONE && bonus != BONUS_NONE ) { XP_ASSERT( bonus <= 4 ); gdk_gc_set_foreground( dctx->drawGC, &dctx->bonusColors[bonus-1] ); gdk_draw_rectangle( DRAW_WHAT(dctx), dctx->drawGC, TRUE, rectInset.left, rectInset.top, rectInset.width+1, rectInset.height+1 ); } else if ( *letter != LETTER_NONE ) { GdkColor* foreground; 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 ); 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 ); } if ( (flags & CELL_ISSTAR) != 0 ) { draw_string_at( dctx, "*", rect->height, rect, XP_GTK_JUST_CENTER, &dctx->black, NULL ); } drawHintBorders( dctx, rect, hintAtts ); if ( (flags & CELL_ISCURSOR) != 0 ) { drawFocusFrame( dctx, rect ); } 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* rect, XP_U16 owner, DrawFocusState dfs ) { GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx; XP_Rect clip = *rect; insetRect( &clip, -1 ); dctx->trayOwner = owner; dctx->topFocus = dfs == DFS_TOP; /* gdk_gc_set_clip_rectangle( dctx->drawGC, (GdkRectangle*)&clip ); */ return XP_TRUE; } /* gtk_draw_trayBegin */ static void gtk_draw_drawTile( DrawCtx* p_dctx, const XP_Rect* rect, const XP_UCHAR* textP, XP_Bitmap bitmap, XP_S16 val, CellFlags flags ) { XP_UCHAR numbuf[3]; gint len; GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx; XP_Rect insetR = *rect; eraseRect( dctx, &insetR ); if ( val >= 0 ) { GdkColor* foreground = &dctx->playerColors[dctx->trayOwner]; XP_Rect formatRect = insetR; insetRect( &insetR, 1 ); formatRect.left += 3; formatRect.width -= 6; gdk_gc_set_foreground( dctx->drawGC, &dctx->tileBack ); gdk_draw_rectangle( DRAW_WHAT(dctx), dctx->drawGC, TRUE, insetR.left, insetR.top, insetR.width, insetR.height ); 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); } } if ((flags & CELL_ISCURSOR) != 0 ) { drawFocusFrame( dctx, rect ); } } /* gtk_draw_drawTile */ static void gtk_draw_drawTileBack( DrawCtx* p_dctx, const XP_Rect* rect, CellFlags flags ) { GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx; 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, &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 ); if ( (flags & CELL_ISCURSOR) != 0 ) { drawFocusFrame( dctx, rect ); } } /* gtk_draw_drawTileBack */ static void gtk_draw_drawTrayDivider( DrawCtx* p_dctx, const XP_Rect* rect, XP_Bool selected ) { GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx; XP_Rect r = *rect; eraseRect( dctx, &r ); ++r.left; r.width -= selected? 2:1; 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; eraseRect( 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 ); */ eraseRect( dctx, rect ); dctx->topFocus = dfs == DFS_TOP; } /* gtk_draw_scoreBegin */ static void gtkDrawDrawRemText( DrawCtx* p_dctx, const XP_Rect* r, XP_U16 nTilesLeft, XP_U16* widthP, XP_U16* heightP ) { GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx; char buf[10]; gint left = r->left; gint top = r->top; XP_Bool draw = !widthP; PangoLayout* layout = layout_for_ht( dctx, r->height ); sprintf( buf, "rem:%d", nTilesLeft ); pango_layout_set_text( layout, buf, strlen(buf) ); if ( draw ) { gdk_draw_layout_with_colors( DRAW_WHAT(dctx), dctx->drawGC, left, top, layout, &dctx->black, NULL ); } else { int width, height; pango_layout_get_pixel_size( layout, &width, &height ); if ( height > HOR_SCORE_HEIGHT ) { height = HOR_SCORE_HEIGHT; } *widthP = width; *heightP = height; } } /* 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 scoreWidthAndText( GtkDrawCtx* XP_UNUSED(dctx), PangoLayout* layout, char* buf, const DrawScoreInfo* dsi, XP_U16* widthP, XP_U16* heightP ) { XP_S16 score = dsi->score; XP_U16 nTilesLeft = dsi->nTilesLeft; XP_Bool isTurn = dsi->isTurn; char* borders = ""; if ( isTurn ) { borders = "*"; } sprintf( buf, "%s%.3d", borders, score ); if ( nTilesLeft < MAX_TRAY_TILES ) { char nbuf[10]; sprintf( nbuf, ":%d", nTilesLeft ); (void)strcat( buf, nbuf ); } (void)strcat( buf, borders ); if ( !!widthP || !!heightP ) { int height, width; pango_layout_set_text( layout, buf, strlen(buf) ); pango_layout_get_pixel_size( layout, &width, &height ); if ( !!widthP ) { *widthP = width; } if ( !!heightP ) { if ( height > HOR_SCORE_HEIGHT ) { height = HOR_SCORE_HEIGHT; } *heightP = height; } } } /* scoreWidthAndText */ static void gtk_draw_measureScoreText( DrawCtx* p_dctx, const XP_Rect* r, const DrawScoreInfo* dsi, XP_U16* width, XP_U16* height ) { GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx; char buf[20]; PangoLayout* layout = layout_for_ht( dctx, r->height ); scoreWidthAndText( dctx, layout, buf, dsi, width, 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_U16 x; XP_U16 width; PangoLayout* layout = layout_for_ht( dctx, rInner->height ); scoreWidthAndText( dctx, layout, scoreBuf, dsi, &width, NULL ); x = rInner->left + ((rInner->width - width) /2); 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 ); eraseRect( dctx, rInner ); } draw_string_at( dctx, scoreBuf, rInner->height - 1, rInner, XP_GTK_JUST_CENTER, &dctx->playerColors[dsi->playerNum], NULL ); if ( ((dsi->flags & CELL_ISCURSOR) != 0) ) { drawFocusFrame( dctx, rOuter ); } } /* 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; if ( score >= 0 ) { sprintf( buf, "%.3d", score ); } else { strcpy( buf, "???" ); } /* gdk_gc_set_clip_rectangle( dctx->drawGC, (GdkRectangle*)rect ); */ localR = *rect; insetRect( &localR, 1 ); eraseRect( dctx, &localR ); ht = localR.height >> 2; draw_string_at( dctx, "Pts:", ht, &localR, XP_GTK_JUST_TOPLEFT, &dctx->black, NULL ); draw_string_at( dctx, buf, ht, &localR, XP_GTK_JUST_BOTTOMRIGHT, &dctx->black, NULL ); if ( !dctx->topFocus && (flags & CELL_ISCURSOR) != 0 ) { drawFocusFrame( dctx, rect ); } } /* 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 ); */ eraseRect( 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... */ eraseRect( dctx, &localR ); insetRect( &localR, 1 ); --localR.width; --localR.height; frameRect( dctx, &localR ); --localR.top; --localR.left; eraseRect( 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 ); 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->red, 0xFFFF, 0x0000, 0x0000 ); return (DrawCtx*)dctx; } /* gtkDrawCtxtMake */ #endif /* PLATFORM_GTK */