merge with drag_n_drop branch: apply patch generated with this command on that branch: svn diff -r 2080:2087

This commit is contained in:
ehouse 2008-03-08 23:16:21 +00:00
parent 4c730be669
commit 0b1f4b8f0a
9 changed files with 605 additions and 485 deletions

View file

@ -62,6 +62,7 @@
#include "LocalizedStrIncludes.h"
#include "boardp.h"
#include "dragdrpp.h"
#include "dbgutil.h"
#define bEND 0x62454e44
@ -73,8 +74,6 @@ extern "C" {
/****************************** prototypes ******************************/
static XP_Bool getCellRect( BoardCtxt* board, XP_U16 col, XP_U16 row,
XP_Rect* rect);
static XP_Bool coordToCell( BoardCtxt* board, XP_U16 x, XP_U16 y,
XP_U16* colP, XP_U16* rowP );
static XP_Bool drawCell( BoardCtxt* board, XP_U16 col, XP_U16 row,
XP_Bool skipBlanks );
static void figureBoardRect( BoardCtxt* board );
@ -82,13 +81,11 @@ static void figureBoardRect( BoardCtxt* board );
static void drawBoard( BoardCtxt* board );
static void invalCell( BoardCtxt* board, XP_U16 col, XP_U16 row );
static void invalCellsUnderRect( BoardCtxt* board, XP_Rect* rect );
static XP_Bool moveTileToBoard( BoardCtxt* board, XP_U16 col, XP_U16 row,
XP_U16 tileIndex, Tile blankFace );
static XP_Bool rectContainsRect( XP_Rect* rect1, XP_Rect* rect2 );
static void boardCellChanged( void* board, XP_U16 turn, XP_U16 col,
XP_U16 row, XP_Bool added );
static void boardTileChanged( void* board, XP_U16 turn, TileBit bits );
static void boardTilesChanged( void* board, XP_U16 turn, XP_S16 index1,
XP_S16 index2 );
static void boardTurnChanged( void* board );
static void boardGameOver( void* board );
static void setArrow( BoardCtxt* board, XP_U16 row, XP_U16 col );
@ -96,8 +93,6 @@ static void setArrowFor( BoardCtxt* board, XP_U16 player, XP_U16 col,
XP_U16 row );
static XP_Bool setArrowVisible( BoardCtxt* board, XP_Bool visible );
static XP_Bool cellOccupied( BoardCtxt* board, XP_U16 col, XP_U16 row,
XP_Bool inclPending );
static void makeMiniWindowForTrade( BoardCtxt* board );
static void makeMiniWindowForText( BoardCtxt* board, const XP_UCHAR* text,
MiniWindowType winType );
@ -115,8 +110,11 @@ static XP_Bool getArrow( BoardCtxt* board, XP_U16* col, XP_U16* row );
static XP_Bool setArrowVisibleFor( BoardCtxt* board, XP_U16 player,
XP_Bool visible );
static XP_Bool board_moveArrow( BoardCtxt* board, XP_Key cursorKey );
static void flipIf( const BoardCtxt* board, XP_U16 col, XP_U16 row,
XP_U16* fCol, XP_U16* fRow );
#ifdef POINTER_SUPPORT
static void drawDragTileIf( BoardCtxt* board );
#endif
static XP_Bool holdsPendingTile( BoardCtxt* board,
XP_U16 pencol, XP_U16 penrow );
#ifdef KEY_SUPPORT
static XP_Bool moveKeyTileToBoard( BoardCtxt* board, XP_Key cursorKey,
@ -170,7 +168,7 @@ board_make( MPFORMAL ModelCtxt* model, ServerCtxt* server, DrawCtx* draw,
/* could just pass in invalCell.... PENDING(eeh) */
model_setBoardListener( model, boardCellChanged, result );
model_setTrayListener( model, boardTileChanged, result );
model_setTrayListener( model, boardTilesChanged, result );
server_setTurnChangeListener( server, boardTurnChanged, result );
server_setGameOverListener( server, boardGameOver, result );
@ -739,7 +737,7 @@ timerFiredForPen( BoardCtxt* board )
const XP_UCHAR* text = (XP_UCHAR*)NULL;
XP_UCHAR buf[80];
if ( board->penDownObject == OBJ_BOARD
if ( (board->penDownObject == OBJ_BOARD) && !dragDropInProgress(board)
#ifdef XWFEATURE_SEARCHLIMIT
&& !board->hintDragInProgress
#endif
@ -958,7 +956,7 @@ board_invalAll( BoardCtxt* board )
board->scoreBoardInvalid = XP_TRUE;
} /* board_invalAll */
static void
void
flipIf( const BoardCtxt* board, XP_U16 col, XP_U16 row,
XP_U16* fCol, XP_U16* fRow )
{
@ -1261,8 +1259,11 @@ drawBoard( BoardCtxt* board )
}
}
/* I doubt the two of these can happen at the same time */
drawTradeWindowIf( board );
#ifdef POINTER_SUPPORT
drawDragTileIf( board );
#endif
draw_objFinished( board->draw, OBJ_BOARD, &board->boardBounds,
dfsFor( board, OBJ_BOARD ) );
@ -1747,10 +1748,20 @@ drawCell( BoardCtxt* board, XP_U16 col, XP_U16 row, XP_Bool skipBlanks )
XP_UCHAR* textP = (XP_UCHAR*)ch;
HintAtts hintAtts;
CellFlags flags = CELL_NONE;
XP_Bool isOrigin;
isEmpty = !model_getTile( model, modelCol, modelRow, showPending,
selPlayer, &tile, &isBlank,
&pending, &recent );
selPlayer, &tile, &isBlank,
&pending, &recent );
if ( dragDropIsBeingDragged( board, col, row, &isOrigin ) ) {
flags |= isOrigin? CELL_DRAGSRC : CELL_DRAGCUR;
if ( isEmpty && !isOrigin ) {
dragDropTileInfo( board, &tile, &isBlank );
pending = XP_TRUE;
recent = XP_FALSE;
isEmpty = XP_FALSE;
}
}
if ( isEmpty ) {
isBlank = XP_FALSE;
@ -1779,8 +1790,7 @@ drawCell( BoardCtxt* board, XP_U16 col, XP_U16 row, XP_Bool skipBlanks )
bonus = util_getSquareBonus( board->util, model, col, row );
hintAtts = figureHintAtts( board, col, row );
if ( isEmpty && (col==board->star_row)
&& (row==board->star_row ) ) {
if ( (col==board->star_row) && (row==board->star_row) ) {
flags |= CELL_ISSTAR;
}
if ( invert ) {
@ -1835,7 +1845,7 @@ figureBoardRect( BoardCtxt* board )
}
} /* figureBoardRect */
static XP_Bool
XP_Bool
coordToCell( BoardCtxt* board, XP_U16 x, XP_U16 y, XP_U16* colP, XP_U16* rowP )
{
XP_U16 col, row, max;
@ -1913,7 +1923,7 @@ invalCell( BoardCtxt* board, XP_U16 col, XP_U16 row )
} /* invalCell */
#if defined POINTER_SUPPORT || defined KEYBOARD_NAV
static XP_Bool
XP_Bool
pointOnSomething( BoardCtxt* board, XP_U16 x, XP_U16 y, BoardObjectType* wp )
{
XP_Bool result = XP_TRUE;
@ -2231,14 +2241,25 @@ static XP_Bool
handlePenDownOnBoard( BoardCtxt* board, XP_U16 x, XP_U16 y )
{
XP_Bool result = XP_FALSE;
XP_U16 col, row;
/* Start a timer no matter what. After it fires we'll decide whether it's
appropriate to handle it. No. That's too expensive */
if ( TRADE_IN_PROGRESS(board) && ptOnTradeWindow( board, x, y ) ) {
return XP_FALSE;
}
util_setTimer( board->util, TIMER_PENDOWN, 0, p_board_timerFired, board );
/* As a first cut, you start a hint-region drag unless the cell is
occupied by a non-committed cell. */
coordToCell( board, x, y, &col, &row );
if ( (board->trayVisState == TRAY_REVEALED)
&& !board->tradeInProgress[board->selPlayer]
&& holdsPendingTile( board, col, row ) ) {
result = dragDropStart( board, OBJ_BOARD, col, row );
#ifdef XWFEATURE_SEARCHLIMIT
if ( board->gi->allowHintRect && board->trayVisState == TRAY_REVEALED ) {
} else if ( board->gi->allowHintRect
&& (board->trayVisState == TRAY_REVEALED) ) {
result = startHintRegionDrag( board, x, y );
}
#endif
@ -2329,8 +2350,9 @@ handleLikeDown( BoardCtxt* board, BoardObjectType onWhich, XP_U16 x, XP_U16 y )
case OBJ_TRAY:
XP_ASSERT( board->trayVisState != TRAY_HIDDEN );
if ( board->trayVisState != TRAY_REVERSED ) {
result = handlePenDownInTray( board, x, y ) || result;
if ( board->trayVisState != TRAY_REVERSED
&& !board->tradeInProgress[board->selPlayer] ) {
result = dragDropStart( board, OBJ_TRAY, x, y ) || result;
}
break;
@ -2385,10 +2407,8 @@ board_handlePenMove( BoardCtxt* board, XP_U16 x, XP_U16 y )
{
XP_Bool result = XP_FALSE;
if ( board->tileDragState.dragInProgress ) {
result = continueTileDrag( board, x, y ) != 0;
} else if ( board->divDragState.dragInProgress ) {
result = continueDividerDrag( board, x, y ) != 0;
if ( dragDropInProgress(board) ) {
result = dragDropContinue( board, x, y ) != 0;
#ifdef XWFEATURE_SEARCHLIMIT
} else if ( board->gi->allowHintRect
&& board->trayVisState == TRAY_REVEALED ) {
@ -2434,7 +2454,7 @@ moveSelTileToBoardXY( BoardCtxt* board, XP_U16 col, XP_U16 row )
return result;
} /* moveSelTileToBoardXY */
static XP_Bool
XP_Bool
cellOccupied( BoardCtxt* board, XP_U16 col, XP_U16 row, XP_Bool inclPending )
{
Tile tile;
@ -2486,41 +2506,37 @@ tryMoveArrow( BoardCtxt* board, XP_U16 col, XP_U16 row )
return result;
} /* tryMoveArrow */
/* Did I tap on a tile on the board that I have not yet committed? If so,
* return it to the tray.
*/
static XP_Bool
tryReplaceTile( BoardCtxt* board, XP_U16 pencol, XP_U16 penrow )
holdsPendingTile( BoardCtxt* board, XP_U16 pencol, XP_U16 penrow )
{
XP_Bool result = XP_FALSE;
XP_S16 index;
XP_U16 col, row;
Tile tile;
XP_Bool ignore, isPending;
XP_U16 modcol, modrow;
flipIf( board, pencol, penrow, &modcol, &modrow );
if ( model_getTile( board->model, modcol, modrow, XP_TRUE,
board->selPlayer, &tile, &ignore, &isPending,
(XP_Bool*)NULL )
&& isPending ) {
XP_S16 count = model_getCurrentMoveCount( board->model,
board->selPlayer );
while ( count-- ) {
index = count;
model_getCurrentMoveTile( board->model, board->selPlayer,
&index, &tile, &col, &row, &ignore );
if ( col == modcol && row == modrow ) {
model_moveBoardToTray( board->model, board->selPlayer,
index );
/* the cursor should show up where the tile used to be so it's
easy to replace it. */
setArrow( board, pencol, penrow );
result = XP_TRUE;
break;
}
}
return model_getTile( board->model, modcol, modrow, XP_TRUE,
board->selPlayer, &tile, &ignore, &isPending,
(XP_Bool*)NULL )
&& isPending;
} /* holdsPendingTile */
/* Did I tap on a tile on the board that I have not yet committed? If so,
* return it to the tray.
*/
XP_Bool
tryReplaceTile( BoardCtxt* board, XP_U16 pencol, XP_U16 penrow )
{
XP_Bool result = XP_FALSE;
if ( holdsPendingTile( board, pencol, penrow ) ) {
XP_U16 modcol, modrow;
flipIf( board, pencol, penrow, &modcol, &modrow );
model_moveBoardToTray( board->model, board->selPlayer,
modcol, modrow, -1 );
setArrow( board, pencol, penrow );
result = XP_TRUE;
}
return result;
} /* tryReplaceTile */
@ -2549,7 +2565,8 @@ exitTradeMode( BoardCtxt* board )
XP_Bool
board_handlePenUp( BoardCtxt* board, XP_U16 x, XP_U16 y )
{
XP_Bool result = XP_FALSE;
XP_Bool draw = XP_FALSE;
XP_Bool dragged = XP_FALSE;
BoardObjectType prevObj = board->penDownObject;
/* prevent timer from firing after pen lifted. Set now rather than later
@ -2557,19 +2574,19 @@ board_handlePenUp( BoardCtxt* board, XP_U16 x, XP_U16 y )
exiting this function (which might give timer time to fire. */
board->penDownObject = OBJ_NONE;
if ( board->tileDragState.dragInProgress ) {
result = endTileDrag( board, x, y );
} else if ( board->divDragState.dragInProgress ) {
result = endDividerDrag( board, x, y );
if ( dragDropInProgress(board) ) {
draw = dragDropEnd( board, x, y, &dragged );
}
if ( dragged ) {
#ifdef XWFEATURE_SEARCHLIMIT
} else if ( board->hintDragInProgress ) {
XP_ASSERT( board->gi->allowHintRect );
result = finishHintRegionDrag( board, x, y );
draw = finishHintRegionDrag( board, x, y ) || draw;
#endif
} else if ( board->penTimerFired ) {
if ( valHintMiniWindowActive( board ) ) {
hideMiniWindow( board, XP_TRUE, MINIWINDOW_VALHINT );
result = XP_TRUE;
draw = XP_TRUE;
}
/* Need to clean up if there's been any dragging happening */
board->penTimerFired = XP_FALSE;
@ -2580,7 +2597,7 @@ board_handlePenUp( BoardCtxt* board, XP_U16 x, XP_U16 y )
switch( onWhich ) {
case OBJ_SCORE:
if ( prevObj == OBJ_SCORE ) {
result = handlePenUpScore( board, x, y );
draw = handlePenUpScore( board, x, y ) || draw;
}
break;
case OBJ_BOARD:
@ -2589,21 +2606,21 @@ board_handlePenUp( BoardCtxt* board, XP_U16 x, XP_U16 y )
if ( TRADE_IN_PROGRESS(board) ) {
if ( ptOnTradeWindow( board, x, y )) {
result = exitTradeMode( board );
draw = exitTradeMode( board ) || draw;
}
} else {
XP_U16 col, row;
coordToCell( board, board->penDownX, board->penDownY,
&col, &row );
result = handleActionInCell( board, col, row );
draw = handleActionInCell( board, col, row ) || draw;
}
}
break;
case OBJ_TRAY:
if ( board->trayVisState == TRAY_REVERSED ) {
result = askRevealTray( board );
draw = askRevealTray( board ) || draw;
} else {
result = handlePenUpTray( board, x, y );
draw = handlePenUpTray( board, x, y ) || draw;
}
break;
default:
@ -2615,7 +2632,7 @@ board_handlePenUp( BoardCtxt* board, XP_U16 x, XP_U16 y )
#ifdef XWFEATURE_SEARCHLIMIT
board->hintDragInProgress = XP_FALSE;
#endif
return result;
return draw;
} /* board_handlePenUp */
#endif /* #ifdef POINTER_SUPPORT */
@ -2914,8 +2931,6 @@ board_focusChanged( BoardCtxt* board, BoardObjectType typ, XP_Bool gained )
draw = invalFocusOwner( board ) || draw;
}
board->focussed = typ;
XP_LOGF( "%s: set focussed to %s", __func__,
BoardObjectType_2str(typ) );
board->focusHasDived = XP_FALSE;
draw = invalFocusOwner( board ) || draw;
} else {
@ -3148,8 +3163,7 @@ replaceLastTile( BoardCtxt* board )
index = -1;
model_getCurrentMoveTile( board->model, board->selPlayer, &index,
&tile, &col, &row, &isBlank );
model_moveBoardToTray( board->model, board->selPlayer, index );
model_moveBoardToTray( board->model, board->selPlayer, col, row, -1 );
flipIf( board, col, row, &col, &row );
setArrow( board, col, row );
@ -3160,9 +3174,9 @@ replaceLastTile( BoardCtxt* board )
return result;
} /* replaceLastTile */
static XP_Bool
XP_Bool
moveTileToBoard( BoardCtxt* board, XP_U16 col, XP_U16 row, XP_U16 tileIndex,
Tile blankFace )
Tile blankFace )
{
if ( cellOccupied( board, col, row, XP_TRUE ) ) {
return XP_FALSE;
@ -3340,13 +3354,13 @@ boardCellChanged( void* p_board, XP_U16 turn, XP_U16 modelCol, XP_U16 modelRow,
} /* boardCellChanged */
static void
boardTileChanged( void* p_board, XP_U16 turn, TileBit bits )
boardTilesChanged( void* p_board, XP_U16 turn, XP_S16 index1, XP_S16 index2 )
{
BoardCtxt* board = (BoardCtxt*)p_board;
if ( turn == board->selPlayer ) {
board_invalTrayTiles( board, bits );
invalTrayTilesBetween( board, index1, index2 );
}
} /* boardTileChanged */
} /* boardTilesChanged */
static void
boardTurnChanged( void* p_board )
@ -3380,6 +3394,109 @@ boardGameOver( void* closure )
util_notifyGameOver( board->util );
} /* boardGameOver */
static void
forceRectToBoard( const BoardCtxt* board, XP_Rect* rect )
{
XP_Rect bounds = board->boardBounds;
if ( rect->left < bounds.left ) {
rect->left = bounds.left;
}
if ( rect->top < bounds.top ) {
rect->top = bounds.top;
}
if ( (rect->left + rect->width) > (bounds.left + bounds.width) ) {
rect->left -= (rect->left+rect->width) - (bounds.left+bounds.width);
}
if ( rect->top + rect->height > bounds.top + bounds.height ) {
rect->top -= (rect->top+rect->height) - (bounds.top+bounds.height);
}
} /* forceRectToBoard */
static void
getDragCellRect( BoardCtxt* board, XP_U16 col, XP_U16 row, XP_Rect* rectP )
{
XP_Rect rect;
XP_U16 tmp;
getCellRect( board, col, row, &rect );
tmp = rect.width;
rect.width = board->trayScaleH;
rect.left -= (rect.width - tmp) / 2;
tmp = rect.height;
rect.height = board->trayScaleV;
rect.top -= (rect.height - tmp) / 2;
*rectP = rect;
forceRectToBoard( board, rectP );
}
#ifdef POINTER_SUPPORT
static void
drawDragTileIf( BoardCtxt* board )
{
if ( dragDropInProgress( board ) ) {
XP_U16 col, row;
if ( dragDropGetBoardTile( board, &col, &row ) ) {
XP_Rect rect;
Tile tile;
XP_Bool isBlank;
XP_UCHAR buf[4];
XP_UCHAR* face;
XP_Bitmap bitmap = NULL;
XP_S16 value;
CellFlags flags;
getDragCellRect( board, col, row, &rect );
dragDropTileInfo( board, &tile, &isBlank );
face = getTileDrawInfo( board, tile, isBlank, &bitmap,
&value, buf, sizeof(buf) );
flags = CELL_DRAGCUR;
if ( isBlank ) {
flags |= CELL_ISBLANK;
}
draw_drawTileMidDrag( board->draw, &rect, face, bitmap, value,
flags );
}
}
} /* drawDragTileIf */
#endif
void
invalDragObj( BoardCtxt* board, const DragObjInfo* di )
{
if ( OBJ_BOARD == di->obj ) {
XP_Rect rect;
getDragCellRect( board, di->u.board.col, di->u.board.row, &rect );
invalCellsUnderRect( board, &rect );
} else if ( OBJ_TRAY == di->obj ) {
board_invalTrayTiles( board, 1 << di->u.tray.index );
}
} /* invalCurObj */
void
invalDragObjRange( BoardCtxt* board, const DragObjInfo* from,
const DragObjInfo* to )
{
invalDragObj( board, from );
if ( NULL != to ) {
invalDragObj( board, to );
if ( (OBJ_TRAY == from->obj) && (OBJ_TRAY == to->obj) ) {
invalTrayTilesBetween( board, from->u.tray.index,
to->u.tray.index );
} else if ( OBJ_TRAY == from->obj ) {
invalTrayTilesAbove( board, from->u.tray.index );
} else if ( OBJ_TRAY == to->obj ) {
invalTrayTilesAbove( board, to->u.tray.index );
}
}
}
#ifdef CPLUS
}
#endif

View file

@ -30,18 +30,29 @@
extern "C" {
#endif
typedef struct TileDragState {
XP_Bool dragInProgress;
typedef struct _DragObjInfo {
BoardObjectType obj;
union {
struct {
XP_U16 col;
XP_U16 row;
} board;
struct {
XP_U16 index;
} tray;
} u;
} DragObjInfo;
XP_Bool wasHilited;
TileBit selectionAtStart;
XP_Bool movePending;
TileBit prevIndex;
} TileDragState;
typedef struct DividerDragState {
typedef struct DragState {
XP_Bool dragInProgress;
} DividerDragState;
XP_Bool dividerOnly; /* special case; most other stuff ignored */
XP_Bool didMove; /* there was change during the drag; not a
tap */
XP_Bool isBlank; /* cache rather than lookup in model */
Tile tile; /* cache rather than lookup in model */
DragObjInfo start;
DragObjInfo cur;
} DragState;
typedef struct BoardArrow { /* gets flipped along with board */
XP_U8 col;
@ -146,8 +157,7 @@ struct BoardCtxt {
XP_Bool dividerInvalid;
XP_Bool scoreBoardInvalid;
TileDragState tileDragState;
DividerDragState divDragState;
DragState dragState;
MiniWindowStuff miniWindowStuff[2];
XP_Bool tradingMiniWindowInvalid;
@ -182,22 +192,41 @@ struct BoardCtxt {
#define TRADE_IN_PROGRESS(b) ((b)->tradeInProgress[(b)->selPlayer]==XP_TRUE)
/* tray-related functions */
XP_Bool handlePenDownInTray( BoardCtxt* board, XP_U16 x, XP_U16 y );
XP_Bool handlePenUpTray( BoardCtxt* board, XP_U16 x, XP_U16 y );
void drawTray( BoardCtxt* board );
TileBit continueTileDrag( BoardCtxt* board, XP_U16 x, XP_U16 y );
XP_Bool endTileDrag( BoardCtxt* board, XP_U16 x, XP_U16 y );
XP_Bool continueDividerDrag( BoardCtxt* board, XP_U16 x, XP_U16 y );
XP_Bool endDividerDrag( BoardCtxt* board, XP_U16 x, XP_U16 y );
XP_Bool moveTileToArrowLoc( BoardCtxt* board, XP_U8 index );
XP_U16 indexForBits( XP_U8 bits );
XP_Bool rectContainsPt( XP_Rect* rect1, XP_S16 x, XP_S16 y );
XP_Bool checkRevealTray( BoardCtxt* board );
void invalTilesUnderRect( BoardCtxt* board, XP_Rect* rect );
void figureTrayTileRect( BoardCtxt* board, XP_U16 index, XP_Rect* rect );
XP_Bool rectsIntersect( const XP_Rect* rect1, const XP_Rect* rect2 );
XP_S16 pointToTileIndex( BoardCtxt* board, XP_U16 x, XP_U16 y,
XP_Bool* onDividerP );
void board_selectPlayer( BoardCtxt* board, XP_U16 newPlayer );
void flipIf( const BoardCtxt* board, XP_U16 col, XP_U16 row,
XP_U16* fCol, XP_U16* fRow );
XP_Bool pointOnSomething( BoardCtxt* board, XP_U16 x, XP_U16 y,
BoardObjectType* wp );
XP_Bool coordToCell( BoardCtxt* board, XP_U16 x, XP_U16 y, XP_U16* colP,
XP_U16* rowP );
XP_Bool cellOccupied( BoardCtxt* board, XP_U16 col, XP_U16 row,
XP_Bool inclPending );
XP_Bool moveTileToBoard( BoardCtxt* board, XP_U16 col, XP_U16 row,
XP_U16 tileIndex, Tile blankFace );
void invalTilesUnderRect( BoardCtxt* board, XP_Rect* rect );
void invalDragObj( BoardCtxt* board, const DragObjInfo* di );
void invalDragObjRange( BoardCtxt* board, const DragObjInfo* from,
const DragObjInfo* to );
void invalTrayTilesAbove( BoardCtxt* board, XP_U16 tileIndex );
void invalTrayTilesBetween( BoardCtxt* board, XP_U16 tileIndex1,
XP_U16 tileIndex2 );
XP_Bool tryReplaceTile( BoardCtxt* board, XP_U16 pencol, XP_U16 penrow );
void moveTileInTray( BoardCtxt* board, XP_U16 moveTo, XP_U16 moveFrom );
XP_UCHAR* getTileDrawInfo( const BoardCtxt* board, Tile tile, XP_Bool isBlank,
XP_Bitmap* bitmap, XP_S16* value,
XP_UCHAR* buf, XP_U16 len );
XP_Bool dividerMoved( BoardCtxt* board, XP_U8 newLoc );
#ifdef KEYBOARD_NAV
XP_Bool tray_moveCursor( BoardCtxt* board, XP_Key cursorKey,

View file

@ -24,6 +24,7 @@ COMMONOBJDIR = ../common/$(PLATFORM)
COMMONSRC = \
$(COMMONDIR)/board.c \
$(COMMONDIR)/dragdrpp.c \
$(COMMONDIR)/scorebdp.c \
$(COMMONDIR)/tray.c \
$(COMMONDIR)/draw.c \
@ -47,6 +48,7 @@ COMMONSRC = \
COMMON1 = \
$(COMMONOBJDIR)/board.o \
$(COMMONOBJDIR)/dragdrpp.o \
$(COMMONOBJDIR)/tray.o \
$(COMMONOBJDIR)/scorebdp.o \
$(COMMONOBJDIR)/draw.o \

View file

@ -37,6 +37,8 @@ typedef enum {
, CELL_ISSTAR = 0x04
, CELL_ISCURSOR = 0x08
, CELL_ISEMPTY = 0x10 /* of a tray tile slot */
, CELL_DRAGSRC = 0x20 /* where drag originated */
, CELL_DRAGCUR = 0x40 /* where drag is now */
, CELL_ALL = 0xFF
} CellFlags;
@ -166,6 +168,12 @@ typedef struct DrawCtxVTable {
/* at least 1 of these two will be null*/
const XP_UCHAR* text, const XP_Bitmap bitmap,
XP_S16 val, CellFlags flags );
#ifdef POINTER_SUPPORT
void DRAW_VTABLE_NAME(drawTileMidDrag) ( DrawCtx* dctx, const XP_Rect* rect,
/* at least 1 of these two will be null*/
const XP_UCHAR* text, const XP_Bitmap bitmap,
XP_S16 val, CellFlags flags );
#endif
void DRAW_VTABLE_NAME(drawTileBack) ( DrawCtx* dctx, const XP_Rect* rect,
CellFlags flags );
void DRAW_VTABLE_NAME(drawTrayDivider) ( DrawCtx* dctx, const XP_Rect* rect,
@ -254,6 +262,10 @@ struct DrawCtx {
#define draw_invertCell( dc, rect ) CALL_DRAW_NAME1(invertCell,(dc),(rect))
#define draw_drawTile( dc, rect, text, bmp, val, hil ) \
CALL_DRAW_NAME5(drawTile,(dc),(rect),(text),(bmp),(val),(hil))
#ifdef POINTER_SUPPORT
#define draw_drawTileMidDrag( dc, rect, text, bmp, val, hil ) \
CALL_DRAW_NAME5(drawTileMidDrag,(dc),(rect),(text),(bmp),(val),(hil))
#endif /* POINTER_SUPPORT */
#define draw_drawTileBack( dc, rect, f ) \
CALL_DRAW_NAME2(drawTileBack, (dc), (rect), (f) )
#define draw_drawTrayDivider( dc, rect, s ) \

View file

@ -48,7 +48,8 @@ static void decrPendingTileCountAt( ModelCtxt* model, XP_U16 col,
XP_U16 row );
static void notifyBoardListeners( ModelCtxt* model, XP_U16 turn,
XP_U16 col, XP_U16 row, XP_Bool added );
static void notifyTrayListeners( ModelCtxt* model, XP_U16 turn, TileBit bits);
static void notifyTrayListeners( ModelCtxt* model, XP_U16 turn,
XP_S16 index1, XP_S16 index2 );
static CellTile getModelTileRaw( ModelCtxt* model, XP_U16 col, XP_U16 row );
static void setModelTileRaw( ModelCtxt* model, XP_U16 col, XP_U16 row,
CellTile tile );
@ -874,30 +875,35 @@ model_getCurrentMoveTile( ModelCtxt* model, XP_S16 turn, XP_S16* index,
*tile = pt->tile & TILE_VALUE_MASK;
} /* model_getCurrentMoveTile */
Tile
model_removePlayerTile( ModelCtxt* model, XP_S16 turn, XP_S16 index )
static Tile
removePlayerTile( ModelCtxt* model, XP_S16 turn, XP_S16 index )
{
PlayerCtxt* player = &model->players[turn];
Tile tile;
short i;
TileBit bits = 0;
if ( index < 0 ) {
index = player->trayTiles.nTiles - 1;
} else {
XP_ASSERT( index < player->trayTiles.nTiles );
}
XP_ASSERT( index < player->trayTiles.nTiles );
tile = player->trayTiles.tiles[index];
bits = 1 << index;
--player->trayTiles.nTiles;
for ( i = index; i < player->trayTiles.nTiles; ++i ) {
player->trayTiles.tiles[i] = player->trayTiles.tiles[i+1];
bits |= 3 << i;
}
notifyTrayListeners( model, turn, bits );
return tile;
} /* removePlayerTile */
Tile
model_removePlayerTile( ModelCtxt* model, XP_S16 turn, XP_S16 index )
{
Tile tile;
PlayerCtxt* player = &model->players[turn];
if ( index < 0 ) {
index = player->trayTiles.nTiles - 1;
}
tile = removePlayerTile( model, turn, index );
notifyTrayListeners( model, turn, index, player->trayTiles.nTiles );
return tile;
} /* model_removePlayerTile */
@ -1003,28 +1009,36 @@ model_moveTrayToBoard( ModelCtxt* model, XP_S16 turn, XP_U16 col, XP_U16 row,
} /* model_moveTrayToBoard */
void
model_moveBoardToTray( ModelCtxt* model, XP_S16 turn, XP_S16 index )
model_moveBoardToTray( ModelCtxt* model, XP_S16 turn,
XP_U16 col, XP_U16 row, XP_U16 trayOffset )
{
XP_S16 index;
PlayerCtxt* player;
short i;
PendingTile* pt;
Tile tile;
player = &model->players[turn];
if ( index < 0 ) {
index = player->nPending - 1;
}
for ( pt = player->pendingTiles, index = 0;
index < player->nPending;
++index, ++pt ) {
if ( pt->col == col && pt->row == row ) {
break;
}
}
/* may be legal to fail to find, but we'd better return now! */
XP_ASSERT( index < player->nPending );
decrPendingTileCountAt( model, col, row );
notifyBoardListeners( model, turn, col, row, XP_FALSE );
pt = &player->pendingTiles[index];
decrPendingTileCountAt( model, pt->col, pt->row );
notifyBoardListeners( model, turn, pt->col, pt->row, XP_FALSE );
tile = pt->tile;
if ( (tile & TILE_BLANK_BIT) != 0 ) {
tile = dict_getBlankTile( model->vol.dict );
}
model_addPlayerTile( model, turn, -1, tile );
model_addPlayerTile( model, turn, trayOffset, tile );
--player->nPending;
for ( i = index; i < player->nPending; ++i ) {
@ -1038,6 +1052,36 @@ model_moveBoardToTray( ModelCtxt* model, XP_S16 turn, XP_S16 index )
invalidateScore( model, turn );
} /* model_moveBoardToTray */
void
model_moveTileOnBoard( ModelCtxt* model, XP_S16 turn, XP_U16 colCur,
XP_U16 rowCur, XP_U16 colNew, XP_U16 rowNew )
{
PlayerCtxt* player = &model->players[turn];
XP_S16 index = player->nPending;
while ( index-- ) {
Tile tile;
XP_U16 tcol, trow;
XP_Bool isBlank;
model_getCurrentMoveTile( model, turn, &index, &tile, &tcol, &trow,
&isBlank );
if ( colCur == tcol && rowCur == trow ) {
PendingTile* pt = &player->pendingTiles[index];
pt->col = colNew;
pt->row = rowNew;
if ( isBlank ) {
pt->tile = TILE_BLANK_BIT | askBlankTile( model, turn );
}
decrPendingTileCountAt( model, colCur, rowCur );
incrPendingTileCountAt( model, colNew, rowNew );
invalidateScore( model, turn );
break;
}
}
}
void
model_resetCurrentTurn( ModelCtxt* model, XP_S16 whose )
{
@ -1047,7 +1091,10 @@ model_resetCurrentTurn( ModelCtxt* model, XP_S16 whose )
player = &model->players[whose];
while ( player->nPending > 0 ) {
model_moveBoardToTray( model, whose, -1 );
model_moveBoardToTray( model, whose,
player->pendingTiles[0].col,
player->pendingTiles[0].row,
-1 );
}
} /* model_resetCurrentTurn */
@ -1104,27 +1151,13 @@ static void
putBackOtherPlayersTiles( ModelCtxt* model, XP_U16 notMyTurn,
XP_U16 col, XP_U16 row )
{
XP_S16 turn, j;
XP_S16 turn;
for ( turn = 0; turn < model->nPlayers; ++turn ) {
PlayerCtxt* player;
if ( turn == notMyTurn ) {
continue;
}
player = &model->players[turn];
for ( j = player->nPending-1; j >= 0; --j ) { /* backwards in case
removed */
PendingTile* pt = &player->pendingTiles[j];
if ( pt->col == col && pt->row == row ) {
/* this one needs to be put back */
model_moveBoardToTray( model, turn, j );
break; /* a player can have only one tile on a square */
}
}
model_moveBoardToTray( model, turn, col, row, -1 );
}
} /* putBackOtherPlayersTiles */
@ -1271,31 +1304,44 @@ model_getPlayerTiles( ModelCtxt* model, XP_S16 turn )
return (const TrayTileSet*)&player->trayTiles;
} /* model_getPlayerTile */
void
model_addPlayerTile( ModelCtxt* model, XP_S16 turn, XP_S16 index, Tile tile )
static void
addPlayerTile( ModelCtxt* model, XP_S16 turn, XP_S16 index, Tile tile )
{
PlayerCtxt* player = &model->players[turn];
short i;
TileBit bits = 0;
XP_ASSERT( player->trayTiles.nTiles < MAX_TRAY_TILES );
if ( index < 0 ) {
index = player->trayTiles.nTiles;
}
XP_ASSERT( index >= 0 );
/* move tiles up to make room */
for ( i = player->trayTiles.nTiles; i > index; --i ) {
player->trayTiles.tiles[i] = player->trayTiles.tiles[i-1];
bits |= (3 << (i-2));
}
++player->trayTiles.nTiles;
player->trayTiles.tiles[index] = tile;
} /* addPlayerTile */
bits |= (1 << index);
notifyTrayListeners( model, turn, bits );
void
model_addPlayerTile( ModelCtxt* model, XP_S16 turn, XP_S16 index, Tile tile )
{
PlayerCtxt* player = &model->players[turn];
if ( index < 0 ) {
index = player->trayTiles.nTiles;
}
addPlayerTile( model, turn, index, tile );
notifyTrayListeners( model, turn, index, player->trayTiles.nTiles );
} /* model_addPlayerTile */
void
model_moveTileOnTray( ModelCtxt* model, XP_S16 turn, XP_S16 indexCur,
XP_S16 indexNew )
{
Tile tile = removePlayerTile( model, turn, indexCur );
addPlayerTile( model, turn, indexNew, tile );
notifyTrayListeners( model, turn, indexCur, indexNew );
} /* model_moveTileOnTray */
static void
assignPlayerTiles( ModelCtxt* model, XP_S16 turn, TrayTileSet* tiles )
{
@ -1367,11 +1413,12 @@ notifyBoardListeners( ModelCtxt* model, XP_U16 turn, XP_U16 col, XP_U16 row,
} /* notifyBoardListeners */
static void
notifyTrayListeners( ModelCtxt* model, XP_U16 turn, TileBit bits )
notifyTrayListeners( ModelCtxt* model, XP_U16 turn, XP_S16 index1,
XP_S16 index2 )
{
if ( model->vol.trayListenerFunc != NULL ) {
(*model->vol.trayListenerFunc)( model->vol.trayListenerData, turn,
bits );
index1, index2 );
}
} /* notifyTrayListeners */

View file

@ -127,6 +127,8 @@ Tile model_getPlayerTile( ModelCtxt* model, XP_S16 turn, XP_S16 index );
Tile model_removePlayerTile( ModelCtxt* model, XP_S16 turn, XP_S16 index );
void model_addPlayerTile( ModelCtxt* model, XP_S16 turn, XP_S16 index,
Tile tile );
void model_moveTileOnTray( ModelCtxt* model, XP_S16 turn, XP_S16 indexCur,
XP_S16 indexNew );
/* As an optimization, return a pointer to the model's array of tiles for a
player. Don't even think about modifying the array!!!! */
@ -134,9 +136,13 @@ const TrayTileSet* model_getPlayerTiles( ModelCtxt* model, XP_S16 turn );
XP_U16 model_getNumTilesInTray( ModelCtxt* model, XP_S16 turn );
XP_U16 model_getNumTilesTotal( ModelCtxt* model, XP_S16 turn );
void model_moveBoardToTray( ModelCtxt* model, XP_S16 turn, XP_S16 index );
void model_moveBoardToTray( ModelCtxt* model, XP_S16 turn,
XP_U16 col, XP_U16 row, XP_U16 trayOffset );
void model_moveTrayToBoard( ModelCtxt* model, XP_S16 turn, XP_U16 col,
XP_U16 row, XP_S16 tileIndex, Tile blankFace );
void model_moveTileOnBoard( ModelCtxt* model, XP_S16 turn, XP_U16 colCur,
XP_U16 rowCur, XP_U16 colNew, XP_U16 rowNew );
XP_S16 model_trayContains( ModelCtxt* model, XP_S16 turn, Tile tile );
@ -184,7 +190,8 @@ typedef void (*BoardListener)(void* data, XP_U16 turn, XP_U16 col,
XP_U16 row, XP_Bool added );
void model_setBoardListener( ModelCtxt* model, BoardListener bl,
void* data );
typedef void (*TrayListener)(void* data, XP_U16 turn, TileBit bits );
typedef void (*TrayListener)( void* data, XP_U16 turn,
XP_S16 index1, XP_S16 index2 );
void model_setTrayListener( ModelCtxt* model, TrayListener bl,
void* data );
void model_foreachPendingCell( ModelCtxt* model, XP_S16 turn,

View file

@ -1,6 +1,6 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/*
* Copyright 1997 - 2007 by Eric House (xwords@eehouse.org). All rights reserved.
* 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
@ -18,6 +18,7 @@
*/
#include "boardp.h"
#include "dragdrpp.h"
#include "engine.h"
#include "draw.h"
#include "strutils.h"
@ -27,13 +28,9 @@ extern "C" {
#endif
/****************************** prototypes ******************************/
static XP_Bool startDividerDrag( BoardCtxt* board );
static XP_Bool startTileDrag( BoardCtxt* board, XP_U8 startIndex );
static void figureDividerRect( BoardCtxt* board, XP_Rect* rect );
static void drawPendingScore( BoardCtxt* board, XP_Bool hasCursor );
static void invalTrayTilesBetween( BoardCtxt* board, XP_U16 tileIndex1,
XP_U16 tileIndex2 );
static XP_Bool endTileDragIndex( BoardCtxt* board, TileBit last );
static XP_U16 countTilesToShow( BoardCtxt* board );
static XP_S16
trayLocToIndex( BoardCtxt* board, XP_U16 loc )
@ -48,7 +45,7 @@ trayLocToIndex( BoardCtxt* board, XP_U16 loc )
return loc;
} /* trayLocToIndex */
static XP_S16
XP_S16
pointToTileIndex( BoardCtxt* board, XP_U16 x, XP_U16 y, XP_Bool* onDividerP )
{
XP_S16 result = -1; /* not on a tile */
@ -99,11 +96,32 @@ figureTrayTileRect( BoardCtxt* board, XP_U16 index, XP_Rect* rect )
}
} /* figureTileRect */
/* When drawing tray mid-drag:
*
* Rule is not to touch the model.
*
* Cases: Tile's been dragged into tray (but not yet dropped.); tile's been
* dragged out of tray (but not yet dropped); and tile's been dragged within
* tray. More's the point, there's an added tile and a removed one. We draw
* the added tile extra, and skip the removed one.
*
* We're walking two arrays at once, backwards. The first is the tile rects
* themselves. If the dirty bit is set, something must get drawn. The second
* is the model's view of tiles augmented by drag-and-drop. D-n-d may have
* removed a tile from the tray (for drawing purposes only), have added one,
* or both (drag-within-tray case). Since a drag lasts only until pen-up,
* there's never more than one tile involved. Adjustment is never by more
* than one.
*
* So while one counter (i) walks the array of rects, we can't use it
* unmodified to fetch from the model. Instead we increment or decrement it
* based on the drag state.
*/
void
drawTray( BoardCtxt* board )
{
XP_Rect tileRect;
short i;
if ( (board->trayInvalBits != 0) || board->dividerInvalid ) {
XP_S16 turn = board->selPlayer;
@ -122,23 +140,23 @@ drawTray( BoardCtxt* board )
}
#endif
/* if ( board->eraseTray ) { */
/* draw_clearRect( board->draw, &board->trayBounds ); */
/* board->eraseTray = XP_FALSE; */
/* } */
if ( (board->trayVisState != TRAY_HIDDEN) && dictionary != NULL ) {
XP_Bool showFaces = board->trayVisState == TRAY_REVEALED;
Tile blank = dict_getBlankTile( dictionary );
if ( turn >= 0 ) {
XP_U16 numInTray = showFaces?
model_getNumTilesInTray( board->model, turn ):
model_getNumTilesTotal( board->model, turn );
XP_S16 i; /* which tile slot are we drawing in */
XP_U16 ddAddedIndx, ddRmvdIndx;
XP_U16 numInTray = countTilesToShow( board );
XP_Bool isBlank;
XP_Bool isADrag = dragDropInProgress( board );
dragDropGetTrayChanges( board, &ddRmvdIndx, &ddAddedIndx );
/* draw in reverse order so drawing happens after
erasing */
for ( i = MAX_TRAY_TILES - 1; i >= 0; --i ) {
for ( i = MAX_TRAY_TILES - 1;
i >= 0; --i ) {
CellFlags flags = CELL_NONE;
XP_U16 mask = 1 << i;
@ -161,28 +179,41 @@ drawTray( BoardCtxt* board )
XP_UCHAR* textP = (XP_UCHAR*)NULL;
XP_U8 traySelBits = board->traySelBits[turn];
XP_S16 value;
Tile tile = model_getPlayerTile( board->model,
turn, i );
Tile tile;
if ( dict_faceIsBitmap( dictionary, tile ) ) {
bitmap = dict_getFaceBitmap( dictionary, tile,
XP_TRUE );
if ( ddAddedIndx == i ) {
dragDropTileInfo( board, &tile, &isBlank );
} else {
textP = buf;
dict_tilesToString( dictionary, &tile, 1,
textP, sizeof(buf) );
XP_U16 modIndex = i;
if ( ddAddedIndx < i ) {
--modIndex;
}
/* while we're right of the removal area,
draw the one from the right to cover. */
if ( ddRmvdIndx <= modIndex /*slotIndx*/ ) {
++modIndex;
}
tile = model_getPlayerTile( board->model,
turn, modIndex );
isBlank = tile == blank;
}
textP = getTileDrawInfo( board, tile, isBlank,
&bitmap, &value,
buf, sizeof(buf) );
if ( board->hideValsInTray
&& !board->showCellValues ) {
value = -1;
} else {
value = dict_getTileValue( dictionary, tile );
}
if ( (traySelBits & (1<<i)) != 0 ) {
if ( isADrag ) {
if ( ddAddedIndx == i ) {
flags |= CELL_HIGHLIGHT;
}
} else if ( (traySelBits & (1<<i)) != 0 ) {
flags |= CELL_HIGHLIGHT;
}
if ( tile == blank ) {
if ( isBlank ) {
flags |= CELL_ISBLANK;
}
@ -199,7 +230,7 @@ drawTray( BoardCtxt* board )
XP_Rect divider;
figureDividerRect( board, &divider );
draw_drawTrayDivider( board->draw, &divider,
board->divDragState.dragInProgress );
dragDropIsDividerDrag(board) );
board->dividerInvalid = XP_FALSE;
}
@ -216,42 +247,69 @@ drawTray( BoardCtxt* board )
} /* drawTray */
XP_UCHAR*
getTileDrawInfo( const BoardCtxt* board, Tile tile, XP_Bool isBlank,
XP_Bitmap* bitmap, XP_S16* value, XP_UCHAR* buf, XP_U16 len )
{
XP_UCHAR* face = NULL;
DictionaryCtxt* dict = model_getDictionary( board->model );
if ( isBlank ) {
tile = dict_getBlankTile( dict );
}
*value = dict_getTileValue( dict, tile );
if ( dict_faceIsBitmap( dict, tile ) ) {
*bitmap = dict_getFaceBitmap( dict, tile, XP_TRUE );
} else {
dict_tilesToString( dict, &tile, 1, buf, len );
face = buf;
}
return face;
}
static XP_U16
countTilesToShow( BoardCtxt* board )
{
XP_U16 numToShow;
XP_S16 selPlayer = board->selPlayer;
XP_U16 ddAddedIndx, ddRemovedIndx;
XP_ASSERT( selPlayer >= 0 );
if ( board->trayVisState == TRAY_REVEALED ) {
numToShow = model_getNumTilesInTray( board->model, selPlayer );
} else {
numToShow = model_getNumTilesTotal( board->model, selPlayer );
}
dragDropGetTrayChanges( board, &ddRemovedIndx, &ddAddedIndx );
if ( ddAddedIndx < MAX_TRAY_TILES ) {
++numToShow;
}
if ( ddRemovedIndx < MAX_TRAY_TILES ) {
--numToShow;
}
XP_ASSERT( numToShow <= MAX_TRAY_TILES );
return numToShow;
} /* countTilesToShow */
static void
drawPendingScore( BoardCtxt* board, XP_Bool hasCursor )
{
/* Draw the pending score down in the last tray's rect */
if ( board->trayVisState == TRAY_REVEALED ) {
if ( countTilesToShow( board ) < MAX_TRAY_TILES ) {
XP_U16 selPlayer = board->selPlayer;
XP_U16 tilesInTray = model_getNumTilesInTray( board->model, selPlayer);
if ( tilesInTray < MAX_TRAY_TILES ) {
XP_S16 turnScore = 0;
XP_Rect lastTileR;
XP_S16 turnScore = 0;
XP_Rect lastTileR;
(void)getCurrentMoveScoreIfLegal( board->model, selPlayer,
(XWStreamCtxt*)NULL, &turnScore );
figureTrayTileRect( board, MAX_TRAY_TILES-1, &lastTileR );
draw_score_pendingScore( board->draw, &lastTileR, turnScore,
selPlayer,
hasCursor?CELL_ISCURSOR:CELL_NONE );
}
(void)getCurrentMoveScoreIfLegal( board->model, selPlayer,
(XWStreamCtxt*)NULL, &turnScore );
figureTrayTileRect( board, MAX_TRAY_TILES-1, &lastTileR );
draw_score_pendingScore( board->draw, &lastTileR, turnScore,
selPlayer,
hasCursor?CELL_ISCURSOR:CELL_NONE );
}
} /* drawPendingScore */
#ifdef DEBUG
static XP_U16
countSelectedTiles( XP_U8 ti )
{
XP_U16 result = 0;
while ( ti != 0 ) {
++result;
ti &= ti-1;
}
return result;
} /* countSelectedTiles */
#endif
static void
figureDividerRect( BoardCtxt* board, XP_Rect* rect )
{
@ -293,66 +351,38 @@ handleTrayDuringTrade( BoardCtxt* board, XP_S16 index )
} /* handleTrayDuringTrade */
static XP_Bool
handleActionInTray( BoardCtxt* board, XP_S16 index, XP_Bool onDivider,
XP_Bool waitPenUp )
handleActionInTray( BoardCtxt* board, XP_S16 index, XP_Bool onDivider )
{
XP_Bool result = XP_FALSE;
XP_U16 selPlayer = board->selPlayer;
if ( onDivider ) {
result = startDividerDrag( board );
} else if ( board->tradeInProgress[selPlayer]
/* && MY_TURN(board) */ ) {
/* do nothing */
} else if ( board->tradeInProgress[selPlayer] ) {
if ( index >= 0 ) {
result = handleTrayDuringTrade( board, index );
}
} else if ( index >= 0 ) {
TileBit newIndex = 1 << index;
BoardArrow* arrow = &board->boardArrow[selPlayer];
if ( !arrow->visible ) {
XP_U8 selFlags = board->traySelBits[selPlayer];
result = moveTileToArrowLoc( board, (XP_U8)index );
if ( !result ) {
TileBit newBits = 1 << index;
XP_U8 selBits = board->traySelBits[selPlayer];
/* Tap on selected tile unselects. If we don't do this,
then there's no way to unselect and so no way to turn
off the placement arrow */
if ( !waitPenUp && newIndex == selFlags ) {
board_invalTrayTiles( board, selFlags );
selFlags = NO_TILES;
board->traySelBits[selPlayer] = selFlags;
result = XP_TRUE;
if ( newBits == selBits ) {
board_invalTrayTiles( board, selBits );
board->traySelBits[selPlayer] = NO_TILES;
} else if ( selBits != 0 ) {
XP_U16 selIndex = indexForBits( selBits );
model_moveTileOnTray( board->model, board->selPlayer,
selIndex, index );
board->traySelBits[selPlayer] = NO_TILES;
} else {
result = startTileDrag( board, newIndex );
if ( !waitPenUp ) {
/* key interface means pen up and down happen in the same
event. No dragging. */
result = endTileDragIndex( board, newIndex ) || result;
}
board_invalTrayTiles( board, newBits );
board->traySelBits[selPlayer] = newBits;
}
}
}
return result;
} /* handleActionInTray */
XP_Bool
handlePenDownInTray( BoardCtxt* board, XP_U16 x, XP_U16 y )
{
XP_Bool onDivider = XP_FALSE;
XP_S16 index = pointToTileIndex( board, x, y, &onDivider );
return handleActionInTray( board, index, onDivider, XP_TRUE );
} /* handlePenDownInTray */
static XP_Bool
handlePenUpTrayInt( BoardCtxt* board, XP_S16 index )
{
XP_Bool result = XP_FALSE;
if ( index >= 0 ) {
XP_U16 selPlayer = board->selPlayer;
BoardArrow* arrow = &board->boardArrow[selPlayer];
if ( arrow->visible ) {
result = moveTileToArrowLoc( board, (XP_U8)index );
result = XP_TRUE;
}
} else if ( index == -(MAX_TRAY_TILES) ) { /* pending score tile */
result = board_commitTurn( board );
@ -361,85 +391,17 @@ handlePenUpTrayInt( BoardCtxt* board, XP_S16 index )
(void)board_replaceTiles( board );
result = XP_TRUE;
}
return result;
} /* handlePenUpTray */
} /* handleActionInTray */
XP_Bool
handlePenUpTray( BoardCtxt* board, XP_U16 x, XP_U16 y )
{
XP_Bool ignore;
XP_S16 index = pointToTileIndex( board, x, y, &ignore );
return handlePenUpTrayInt( board, index );
XP_Bool onDivider;
XP_S16 index = pointToTileIndex( board, x, y, &onDivider );
return handleActionInTray( board, index, onDivider );
} /* handlePenUpTray */
static XP_Bool
startTileDrag( BoardCtxt* board, TileBit startBit/* , XP_U16 x, XP_U16 y */ )
{
XP_Bool result = XP_FALSE;
XP_U16 turn = board->selPlayer;
XP_U8 startSel = board->traySelBits[turn];
TileDragState* state = &board->tileDragState;
XP_ASSERT( countSelectedTiles( startBit ) == 1 );
XP_ASSERT( !state->dragInProgress );
state->wasHilited = startSel == startBit;
state->selectionAtStart = startSel;
state->movePending = XP_TRUE;
state->dragInProgress = XP_TRUE;
state->prevIndex = board->traySelBits[turn] = startBit;
if ( !state->wasHilited ) {
board_invalTrayTiles( board, (TileBit)(startBit | startSel) );
result = XP_TRUE;
}
return result;
} /* startTileDrag */
static void
moveTileInTray( BoardCtxt* board, TileBit prevTile, TileBit newTile )
{
XP_S16 selPlayer = board->selPlayer;
ModelCtxt* model = board->model;
XP_U16 moveTo = indexForBits( prevTile );
XP_U16 moveFrom = indexForBits( newTile );
Tile tile;
XP_U16 dividerLoc;
tile = model_removePlayerTile( model, selPlayer, moveFrom );
model_addPlayerTile( model, selPlayer, moveTo, tile );
dividerLoc = board->dividerLoc[selPlayer];
if ( moveTo < dividerLoc || moveFrom < dividerLoc ) {
server_resetEngine( board->server, selPlayer );
}
} /* moveTileInTray */
TileBit
continueTileDrag( BoardCtxt* board, XP_U16 x, XP_U16 y )
{
TileDragState* state = &board->tileDragState;
TileBit overTile = 0;
XP_S16 index = pointToTileIndex( board, x, y, (XP_Bool*)NULL );
if ( index >= 0 ) {
overTile = 1 << index;
if ( overTile != state->prevIndex ) {
moveTileInTray( board, overTile, state->prevIndex );
state->movePending = XP_FALSE;
state->wasHilited = XP_FALSE; // so we won't deselect
state->prevIndex = board->traySelBits[board->selPlayer] = overTile;
}
}
return overTile;
} /* continueTileDrag */
XP_U16
indexForBits( XP_U8 bits )
{
@ -455,123 +417,48 @@ indexForBits( XP_U8 bits )
return result;
} /* indexForBits */
static XP_Bool
endTileDragIndex( BoardCtxt* board, TileBit last )
{
XP_Bool result = XP_FALSE;
XP_U16 selPlayer = board->selPlayer;
TileDragState* state = &board->tileDragState;
if ( state->movePending ) { /* no drag took place */
if ( state->wasHilited ) { /* if the user just clicked; deselect */
board_invalTrayTiles( board, state->selectionAtStart );
board->traySelBits[selPlayer] = NO_TILES;
result = XP_TRUE;
} else if ( (last > 0)
&& !board->boardArrow[selPlayer].visible
&& (state->selectionAtStart != NO_TILES ) ) {
if ( model_getCurrentMoveCount( board->model, selPlayer) == 0 ) {
moveTileInTray( board, last, state->selectionAtStart );
board->traySelBits[selPlayer] = NO_TILES;
} else {
board_invalTrayTiles(
board,
(TileBit)(state->selectionAtStart|last) );
board->traySelBits[selPlayer] = last;
}
result = XP_TRUE;
}
} else {
board_invalTrayTiles( board, state->prevIndex );
board->traySelBits[selPlayer] = NO_TILES;
result = XP_TRUE;
}
state->dragInProgress = XP_FALSE;
return result;
} /* endTileDragIndex */
XP_Bool
endTileDrag( BoardCtxt* board, XP_U16 x, XP_U16 y )
{
TileBit newTile = continueTileDrag( board, x, y );
return endTileDragIndex( board, newTile );
} /* endTileDrag */
static XP_Bool
startDividerDrag( BoardCtxt* board )
{
board->divDragState.dragInProgress = XP_TRUE;
board->dividerInvalid = XP_TRUE;
return XP_TRUE;
} /* startDividerDrag */
static void
dividerMoved( BoardCtxt* board, XP_U8 newLoc )
{
XP_U8 oldLoc = board->dividerLoc[board->selPlayer];
board->dividerLoc[board->selPlayer] = newLoc;
XP_Bool moved = oldLoc != newLoc;
if ( moved ) {
board->dividerLoc[board->selPlayer] = newLoc;
/* This divider's index corresponds to the tile it's to the left of, and
there's no need to invalidate any tiles to the left of the uppermore
divider position. */
if ( oldLoc > newLoc ) {
--oldLoc;
} else {
--newLoc;
/* This divider's index corresponds to the tile it's to the left of, and
there's no need to invalidate any tiles to the left of the uppermore
divider position. */
if ( oldLoc > newLoc ) {
--oldLoc;
} else {
--newLoc;
}
invalTrayTilesBetween( board, newLoc, oldLoc );
board->dividerInvalid = XP_TRUE;
/* changed number of available tiles */
board_resetEngine( board );
}
invalTrayTilesBetween( board, newLoc, oldLoc );
board->dividerInvalid = XP_TRUE;
/* changed number of available tiles */
board_resetEngine( board );
return moved;
} /* dividerMoved */
XP_Bool
continueDividerDrag( BoardCtxt* board, XP_U16 x, XP_U16 y )
{
XP_U8 newOffset;
XP_U16 trayScale = board->trayScaleH;
XP_Bool result = XP_FALSE;
XP_ASSERT( board->divDragState.dragInProgress );
/* Pen might have been dragged out of the tray */
if ( rectContainsPt( &board->trayBounds, x, y ) ) {
x -= board->trayBounds.left;
newOffset = x / trayScale;
if ( (x % trayScale) > (trayScale/2) ) {
++newOffset;
}
result = newOffset != board->dividerLoc[board->selPlayer];
if ( result ) {
dividerMoved( board, newOffset );
}
}
return result;
} /* continueDividerDrag */
XP_Bool
endDividerDrag( BoardCtxt* board, XP_U16 x, XP_U16 y )
{
XP_Bool result = XP_TRUE; /* b/c hilited state looks different */
(void)continueDividerDrag( board, x, y );
board->dividerInvalid = XP_TRUE;
board->divDragState.dragInProgress = XP_FALSE;
return result;
} /* endDividerDrag */
void
board_invalTrayTiles( BoardCtxt* board, TileBit what )
{
board->trayInvalBits |= what;
} /* invalTrayTiles */
static void
void
invalTrayTilesAbove( BoardCtxt* board, XP_U16 tileIndex )
{
TileBit bits = 0;
while ( tileIndex < MAX_TRAY_TILES ) {
bits |= 1 << tileIndex++;
}
board_invalTrayTiles( board, bits );
}
void
invalTrayTilesBetween( BoardCtxt* board, XP_U16 tileIndex1,
XP_U16 tileIndex2 )
{
@ -710,7 +597,7 @@ board_moveDivider( BoardCtxt* board, XP_Bool right )
loc += right? 1:-1;
loc %= MAX_TRAY_TILES + 1;
dividerMoved( board, loc );
(void)dividerMoved( board, loc );
}
return result;
} /* board_moveDivider */

View file

@ -1,6 +1,7 @@
/* -*- 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.
* 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
@ -71,7 +72,7 @@ eraseRect( GtkDrawCtx* dctx, const XP_Rect* rect )
} /* eraseRect */
static void
frameRect( GtkDrawCtx* dctx, XP_Rect* rect )
frameRect( GtkDrawCtx* dctx, const XP_Rect* rect )
{
gdk_draw_rectangle( DRAW_WHAT(dctx),
dctx->drawGC, FALSE, rect->left, rect->top,
@ -431,55 +432,46 @@ gtk_draw_drawCell( DrawCtx* p_dctx, const XP_Rect* rect, const XP_UCHAR* letter,
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 );
/* 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) != 0 || ( !!letter && *letter == LETTER_NONE ) ) {
if ( bonus != BONUS_NONE ) {
gdk_gc_set_foreground( dctx->drawGC, &dctx->bonusColors[bonus-1] );
gdk_draw_rectangle( DRAW_WHAT(dctx),
dctx->drawGC,
TRUE,
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;
} 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 );
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 );
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 );
}
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 ) {
@ -525,15 +517,18 @@ gtk_draw_trayBegin( DrawCtx* p_dctx, const XP_Rect* rect, XP_U16 owner,
} /* 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 )
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;
eraseRect( dctx, &insetR );
if ( clearBack ) {
eraseRect( dctx, &insetR );
}
if ( val >= 0 ) {
GdkColor* foreground = &dctx->playerColors[dctx->trayOwner];
@ -541,17 +536,18 @@ gtk_draw_drawTile( DrawCtx* p_dctx, const XP_Rect* rect, const XP_UCHAR* textP,
insetRect( &insetR, 1 );
if ( clearBack ) {
gdk_gc_set_foreground( dctx->drawGC, &dctx->tileBack );
gdk_draw_rectangle( DRAW_WHAT(dctx),
dctx->drawGC,
XP_TRUE,
insetR.left, insetR.top, insetR.width,
insetR.height );
}
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,
@ -593,6 +589,25 @@ gtk_draw_drawTile( DrawCtx* p_dctx, const XP_Rect* rect, const XP_UCHAR* textP,
} /* gtk_draw_drawTile */
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, CellFlags flags )
{
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 )
@ -635,7 +650,7 @@ gtk_draw_drawTrayDivider( DrawCtx* p_dctx, const XP_Rect* rect,
++r.left;
r.width -= selected? 2:1;
if ( selected ) {
--r.height;
--r.height;
}
gdk_gc_set_foreground( dctx->drawGC, &dctx->black );
@ -1030,6 +1045,9 @@ gtkDrawCtxtMake( GtkWidget* drawing_area, GtkAppGlobals* globals )
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 );

View file

@ -108,12 +108,13 @@ button_press_event( GtkWidget* XP_UNUSED(widget), GdkEventButton *event,
{
XP_Bool redraw, handled;
globals->mouseDown = XP_TRUE;
redraw = board_handlePenDown( globals->cGlobals.game.board,
if ( !globals->mouseDown ) {
globals->mouseDown = XP_TRUE;
redraw = board_handlePenDown( globals->cGlobals.game.board,
event->x, event->y, &handled );
if ( redraw ) {
board_draw( globals->cGlobals.game.board );
if ( redraw ) {
board_draw( globals->cGlobals.game.board );
}
}
return 1;
} /* button_press_event */