From 0b1f4b8f0a7f5bd62eb5058ac50474391460b17c Mon Sep 17 00:00:00 2001 From: ehouse Date: Sat, 8 Mar 2008 23:16:21 +0000 Subject: [PATCH] merge with drag_n_drop branch: apply patch generated with this command on that branch: svn diff -r 2080:2087 --- xwords4/common/board.c | 273 +++++++++++++++++------- xwords4/common/boardp.h | 67 ++++-- xwords4/common/config.mk | 2 + xwords4/common/draw.h | 12 ++ xwords4/common/model.c | 147 ++++++++----- xwords4/common/model.h | 11 +- xwords4/common/tray.c | 445 +++++++++++++++------------------------ xwords4/linux/gtkdraw.c | 122 ++++++----- xwords4/linux/gtkmain.c | 11 +- 9 files changed, 605 insertions(+), 485 deletions(-) diff --git a/xwords4/common/board.c b/xwords4/common/board.c index 7d9e29473..0c5e2bd39 100644 --- a/xwords4/common/board.c +++ b/xwords4/common/board.c @@ -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 diff --git a/xwords4/common/boardp.h b/xwords4/common/boardp.h index e8534155d..fdd71da71 100644 --- a/xwords4/common/boardp.h +++ b/xwords4/common/boardp.h @@ -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, diff --git a/xwords4/common/config.mk b/xwords4/common/config.mk index 92fb33cef..7ec6d9057 100644 --- a/xwords4/common/config.mk +++ b/xwords4/common/config.mk @@ -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 \ diff --git a/xwords4/common/draw.h b/xwords4/common/draw.h index 4fa54d0e1..104c7252c 100644 --- a/xwords4/common/draw.h +++ b/xwords4/common/draw.h @@ -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 ) \ diff --git a/xwords4/common/model.c b/xwords4/common/model.c index 9651a20a9..81ede84f5 100644 --- a/xwords4/common/model.c +++ b/xwords4/common/model.c @@ -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 */ diff --git a/xwords4/common/model.h b/xwords4/common/model.h index c12d342c0..15d6b2108 100644 --- a/xwords4/common/model.h +++ b/xwords4/common/model.h @@ -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, diff --git a/xwords4/common/tray.c b/xwords4/common/tray.c index 040d3ccf4..4c84fdbd5 100644 --- a/xwords4/common/tray.c +++ b/xwords4/common/tray.c @@ -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<draw, ÷r, - 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 */ diff --git a/xwords4/linux/gtkdraw.c b/xwords4/linux/gtkdraw.c index 8347d3fff..53b102a8e 100644 --- a/xwords4/linux/gtkdraw.c +++ b/xwords4/linux/gtkdraw.c @@ -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 ); diff --git a/xwords4/linux/gtkmain.c b/xwords4/linux/gtkmain.c index 844813d88..0ccd01609 100644 --- a/xwords4/linux/gtkmain.c +++ b/xwords4/linux/gtkmain.c @@ -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 */