mirror of
git://xwords.git.sourceforge.net/gitroot/xwords/xwords
synced 2024-12-30 10:26:58 +01:00
abca6a4092
not dived and tray or scoreboard focussed. This lets platform decide to display top-level focus via mods to all elements. Second, when moving focus to top level from dived, claim not to have handled the key event (but return true if needs redraw). This allows platform to handle shifting focus without requiring callback (which removed.)
732 lines
23 KiB
C
732 lines
23 KiB
C
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
|
|
/*
|
|
* Copyright 1997 - 2006 by Eric House (xwords@eehouse.org). All rights reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
#include "boardp.h"
|
|
#include "engine.h"
|
|
#include "draw.h"
|
|
#include "strutils.h"
|
|
|
|
#ifdef CPLUS
|
|
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_S16
|
|
trayLocToIndex( BoardCtxt* board, XP_U16 loc )
|
|
{
|
|
if ( loc >= model_getNumTilesInTray( board->model,
|
|
board->selPlayer ) ) {
|
|
loc *= -1;
|
|
/* (0 * -1) is still 0, so reduce by 1. Will need to adjust
|
|
below. NOTE: this is something of a hack.*/
|
|
--loc;
|
|
}
|
|
return loc;
|
|
} /* trayLocToIndex */
|
|
|
|
static XP_S16
|
|
pointToTileIndex( BoardCtxt* board, XP_U16 x, XP_U16 y, XP_Bool* onDividerP )
|
|
{
|
|
XP_S16 result = -1; /* not on a tile */
|
|
XP_Rect divider;
|
|
XP_Rect biggerRect;
|
|
XP_Bool onDivider;
|
|
|
|
figureDividerRect( board, ÷r );
|
|
|
|
/* The divider rect is narrower and kinda hard to tap on. Let's expand
|
|
it just for this test */
|
|
biggerRect = divider;
|
|
biggerRect.left -= 2;
|
|
biggerRect.width += 4;
|
|
onDivider = rectContainsPt( &biggerRect, x, y );
|
|
|
|
if ( !onDivider ) {
|
|
if ( x > divider.left ) {
|
|
XP_ASSERT( divider.width == board->dividerWidth );
|
|
x -= divider.width;
|
|
}
|
|
|
|
XP_ASSERT( x >= board->trayBounds.left );
|
|
x -= board->trayBounds.left;
|
|
result = x / board->trayScaleH;
|
|
|
|
result = trayLocToIndex( board, result );
|
|
}
|
|
|
|
if ( onDividerP != NULL ) {
|
|
*onDividerP = onDivider;
|
|
}
|
|
|
|
return result;
|
|
} /* pointToTileIndex */
|
|
|
|
static void
|
|
figureTrayTileRect( BoardCtxt* board, XP_U16 index, XP_Rect* rect )
|
|
{
|
|
rect->left = board->trayBounds.left + (index * board->trayScaleH);
|
|
rect->top = board->trayBounds.top/* + 1 */;
|
|
|
|
rect->width = board->trayScaleH;
|
|
rect->height = board->trayScaleV;
|
|
|
|
if ( board->dividerLoc[board->selPlayer] <= index ) {
|
|
rect->left += board->dividerWidth;
|
|
}
|
|
} /* figureTileRect */
|
|
|
|
void
|
|
drawTray( BoardCtxt* board )
|
|
{
|
|
XP_Rect tileRect;
|
|
short i;
|
|
|
|
if ( (board->trayInvalBits != 0) || board->dividerInvalid ) {
|
|
XP_S16 turn = board->selPlayer;
|
|
|
|
if ( draw_trayBegin( board->draw, &board->trayBounds, turn,
|
|
dfsFor( board, OBJ_TRAY ) ) ) {
|
|
DictionaryCtxt* dictionary = model_getDictionary( board->model );
|
|
XP_S16 cursorBits = 0;
|
|
#ifdef KEYBOARD_NAV
|
|
if ( board->focussed == OBJ_TRAY ) {
|
|
if ( board->focusHasDived ) {
|
|
cursorBits = 1 << board->trayCursorLoc[turn];
|
|
} else {
|
|
cursorBits = ALLTILES;
|
|
}
|
|
}
|
|
#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 );
|
|
|
|
/* draw in reverse order so drawing happens after
|
|
erasing */
|
|
for ( i = MAX_TRAY_TILES - 1; i >= 0; --i ) {
|
|
CellFlags flags = CELL_NONE;
|
|
XP_U16 mask = 1 << i;
|
|
|
|
if ( (board->trayInvalBits & mask) == 0 ) {
|
|
continue;
|
|
}
|
|
#ifdef KEYBOARD_NAV
|
|
if ( (cursorBits & mask) != 0 ) {
|
|
flags |= CELL_ISCURSOR;
|
|
}
|
|
#endif
|
|
figureTrayTileRect( board, i, &tileRect );
|
|
|
|
if ( i >= numInTray ) {
|
|
draw_drawTile( board->draw, &tileRect, NULL,
|
|
NULL, -1, flags | CELL_ISEMPTY );
|
|
} else if ( showFaces ) {
|
|
XP_UCHAR buf[4];
|
|
XP_Bitmap bitmap = NULL;
|
|
XP_UCHAR* textP = (XP_UCHAR*)NULL;
|
|
XP_U8 traySelBits = board->traySelBits[turn];
|
|
XP_S16 value;
|
|
Tile tile = model_getPlayerTile( board->model,
|
|
turn, i );
|
|
|
|
if ( dict_faceIsBitmap( dictionary, tile ) ) {
|
|
bitmap = dict_getFaceBitmap( dictionary, tile,
|
|
XP_TRUE );
|
|
} else {
|
|
textP = buf;
|
|
dict_tilesToString( dictionary, &tile, 1,
|
|
textP, sizeof(buf) );
|
|
}
|
|
if ( board->hideValsInTray ) {
|
|
value = -1;
|
|
} else {
|
|
value = dict_getTileValue( dictionary, tile );
|
|
}
|
|
|
|
if ( (traySelBits & (1<<i)) != 0 ) {
|
|
flags |= CELL_HIGHLIGHT;
|
|
}
|
|
if ( tile == blank ) {
|
|
flags |= CELL_ISBLANK;
|
|
}
|
|
|
|
draw_drawTile( board->draw, &tileRect, textP,
|
|
bitmap, value, flags );
|
|
} else {
|
|
draw_drawTileBack( board->draw, &tileRect,
|
|
flags );
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( (board->dividerWidth > 0) && board->dividerInvalid ) {
|
|
XP_Rect divider;
|
|
figureDividerRect( board, ÷r );
|
|
draw_drawTrayDivider( board->draw, ÷r,
|
|
board->divDragState.dragInProgress );
|
|
board->dividerInvalid = XP_FALSE;
|
|
}
|
|
|
|
drawPendingScore( board,
|
|
(cursorBits & (1<<(MAX_TRAY_TILES-1))) != 0 );
|
|
|
|
#ifdef KEYBOARD_NAV
|
|
if ( (cursorBits != 0) && (cursorBits != ALLTILES) ) {
|
|
figureTrayTileRect( board, indexForBits(cursorBits),
|
|
&tileRect );
|
|
draw_drawCursor( board->draw, OBJ_TRAY, &tileRect );
|
|
}
|
|
#endif
|
|
}
|
|
|
|
draw_objFinished( board->draw, OBJ_TRAY, &board->trayBounds,
|
|
dfsFor( board, OBJ_TRAY ) );
|
|
|
|
board->trayInvalBits = 0;
|
|
}
|
|
}
|
|
|
|
} /* drawTray */
|
|
|
|
static void
|
|
drawPendingScore( BoardCtxt* board, XP_Bool hasCursor )
|
|
{
|
|
/* Draw the pending score down in the last tray's rect */
|
|
if ( board->trayVisState == TRAY_REVEALED ) {
|
|
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;
|
|
|
|
(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 )
|
|
{
|
|
figureTrayTileRect( board, board->dividerLoc[board->selPlayer], rect );
|
|
rect->left -= board->dividerWidth;
|
|
rect->width = board->dividerWidth;
|
|
} /* figureDividerRect */
|
|
|
|
void
|
|
invalTilesUnderRect( BoardCtxt* board, XP_Rect* rect )
|
|
{
|
|
XP_U16 i;
|
|
XP_Rect locRect;
|
|
|
|
for ( i = 0; i < MAX_TRAY_TILES; ++i ) {
|
|
figureTrayTileRect( board, i, &locRect );
|
|
if ( rectsIntersect( rect, &locRect ) ) {
|
|
board_invalTrayTiles( board, (TileBit)(1 << i) );
|
|
}
|
|
}
|
|
|
|
figureDividerRect( board, &locRect );
|
|
if ( rectsIntersect( rect, &locRect ) ) {
|
|
board->dividerInvalid = XP_TRUE;
|
|
}
|
|
} /* invalTilesUnderRect */
|
|
|
|
static XP_Bool
|
|
handleTrayDuringTrade( BoardCtxt* board, XP_S16 index )
|
|
{
|
|
TileBit bits;
|
|
|
|
XP_ASSERT( index >= 0 );
|
|
|
|
bits = 1 << index;
|
|
board->traySelBits[board->selPlayer] ^= bits;
|
|
board_invalTrayTiles( board, bits );
|
|
return XP_TRUE;
|
|
} /* handleTrayDuringTrade */
|
|
|
|
static XP_Bool
|
|
handleActionInTray( BoardCtxt* board, XP_S16 index, XP_Bool onDivider,
|
|
XP_Bool waitPenUp )
|
|
{
|
|
XP_Bool result = XP_FALSE;
|
|
XP_U16 selPlayer = board->selPlayer;
|
|
|
|
if ( onDivider ) {
|
|
result = startDividerDrag( board );
|
|
} else if ( board->tradeInProgress[selPlayer]
|
|
/* && MY_TURN(board) */ ) {
|
|
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];
|
|
/* 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;
|
|
} 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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
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 );
|
|
}
|
|
} else if ( index == -(MAX_TRAY_TILES) ) { /* pending score tile */
|
|
result = board_commitTurn( board );
|
|
} else if ( index < 0 ) { /* other empty area */
|
|
/* it better be true */
|
|
(void)board_replaceTiles( board );
|
|
result = XP_TRUE;
|
|
}
|
|
|
|
return result;
|
|
} /* handlePenUpTray */
|
|
|
|
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 );
|
|
} /* 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 )
|
|
{
|
|
XP_U16 result = 0;
|
|
XP_U16 mask = 1;
|
|
|
|
XP_ASSERT( bits != 0 ); /* otherwise loops forever */
|
|
|
|
while ( (mask & bits) == 0 ) {
|
|
++result;
|
|
mask <<= 1;
|
|
}
|
|
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;
|
|
|
|
/* 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 );
|
|
} /* 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
|
|
invalTrayTilesBetween( BoardCtxt* board, XP_U16 tileIndex1,
|
|
XP_U16 tileIndex2 )
|
|
{
|
|
TileBit bits = 0;
|
|
|
|
if ( tileIndex1 > tileIndex2 ) {
|
|
XP_U16 tmp = tileIndex1;
|
|
tileIndex1 = tileIndex2;
|
|
tileIndex2 = tmp;
|
|
}
|
|
|
|
while ( tileIndex1 <= tileIndex2 ) {
|
|
bits |= (1 << tileIndex1);
|
|
++tileIndex1;
|
|
}
|
|
board_invalTrayTiles( board, bits );
|
|
} /* invalTrayTilesBetween */
|
|
|
|
XP_Bool
|
|
board_juggleTray( BoardCtxt* board )
|
|
{
|
|
XP_Bool result = XP_FALSE;
|
|
XP_S16 turn = board->selPlayer;
|
|
|
|
if ( checkRevealTray( board ) ) {
|
|
XP_S16 nTiles;
|
|
XP_U16 dividerLoc = board->dividerLoc[board->selPlayer];
|
|
ModelCtxt* model = board->model;
|
|
|
|
nTiles = model_getNumTilesInTray( model, turn ) - dividerLoc;
|
|
if ( nTiles > 1 ) {
|
|
XP_S16 i;
|
|
Tile tmpT[MAX_TRAY_TILES];
|
|
XP_U16 newT[MAX_TRAY_TILES];
|
|
|
|
/* loop until there'll be change */
|
|
while ( !randIntArray( newT, nTiles ) ) {
|
|
}
|
|
|
|
/* save copies of the tiles in juggled order */
|
|
for ( i = 0; i < nTiles; ++i ) {
|
|
tmpT[i] = model_getPlayerTile( model, turn,
|
|
(Tile)(dividerLoc + newT[i]) );
|
|
}
|
|
|
|
/* delete tiles off right end; put juggled ones back on the other */
|
|
for ( i = nTiles - 1; i >= 0; --i ) {
|
|
(void)model_removePlayerTile( model, turn, -1 );
|
|
model_addPlayerTile( model, turn, dividerLoc, tmpT[i] );
|
|
}
|
|
board->traySelBits[turn] = 0;
|
|
result = XP_TRUE;
|
|
}
|
|
}
|
|
return result;
|
|
} /* board_juggleTray */
|
|
|
|
#ifdef KEYBOARD_NAV
|
|
XP_Bool
|
|
tray_moveCursor( BoardCtxt* board, XP_Key cursorKey, XP_Bool* up )
|
|
{
|
|
XP_Bool result;
|
|
XP_U16 selPlayer = board->selPlayer;
|
|
XP_S16 pos;
|
|
|
|
switch (cursorKey ) {
|
|
case XP_CURSOR_KEY_UP:
|
|
case XP_CURSOR_KEY_DOWN:
|
|
*up = XP_TRUE;
|
|
/* moving the divider needs to be hard to do accidentally since it
|
|
confuses users when juggle and hint stop working. But all things
|
|
must be possible via keyboard on devices that don't have
|
|
touchscreens. Probably need a new keytype XP_CURSOR_KEY_ALTDOWN
|
|
etc. */
|
|
/* result = board_moveDivider( board, XP_FALSE ); */
|
|
/* result = board_moveDivider( board, XP_TRUE ); */
|
|
break;
|
|
case XP_CURSOR_KEY_RIGHT:
|
|
case XP_CURSOR_KEY_LEFT:
|
|
board_invalTrayTiles( board, 1 << board->trayCursorLoc[selPlayer] );
|
|
|
|
pos = board->trayCursorLoc[selPlayer];
|
|
/* Loop in order to skip all empty tile slots but one */
|
|
for ( ; ; ) {
|
|
pos += (cursorKey == XP_CURSOR_KEY_RIGHT ? 1 : -1);
|
|
if ( pos < 0 || pos >= MAX_TRAY_TILES ) {
|
|
*up = XP_TRUE;
|
|
} else {
|
|
/* Revisit this when able to never draw the cursor in a place
|
|
this won't allow it, e.g. when the tiles move after a
|
|
hint */
|
|
/* if ( board->trayVisState == TRAY_REVEALED ) { */
|
|
/* XP_U16 count = model_getNumTilesInTray( board->model, */
|
|
/* selPlayer ); */
|
|
/* if ( (pos > count) && (pos < MAX_TRAY_TILES-1) ) { */
|
|
/* continue; */
|
|
/* } */
|
|
/* } */
|
|
board->trayCursorLoc[selPlayer] = pos;
|
|
board_invalTrayTiles( board, 1 << pos );
|
|
}
|
|
break;
|
|
}
|
|
board_invalTrayTiles( board, 1 << board->trayCursorLoc[selPlayer] );
|
|
result = XP_TRUE;
|
|
break;
|
|
default:
|
|
XP_ASSERT(0);
|
|
break;
|
|
}
|
|
|
|
return result;
|
|
} /* tray_moveCursor */
|
|
|
|
XP_Bool
|
|
tray_keyAction( BoardCtxt* board )
|
|
{
|
|
TileBit cursor = 1 << board->trayCursorLoc[board->selPlayer];
|
|
XP_Bool result;
|
|
if ( !!cursor ) {
|
|
XP_S16 index = trayLocToIndex( board, indexForBits( cursor ) );
|
|
result = handleActionInTray( board, index, XP_FALSE, XP_FALSE )
|
|
|| handlePenUpTrayInt( board, index );
|
|
} else {
|
|
result = XP_FALSE;
|
|
}
|
|
|
|
return result;
|
|
} /* tray_keyAction */
|
|
#endif
|
|
|
|
#if defined FOR_GREMLINS || defined KEYBOARD_NAV
|
|
XP_Bool
|
|
board_moveDivider( BoardCtxt* board, XP_Bool right )
|
|
{
|
|
XP_Bool result = board->trayVisState == TRAY_REVEALED;
|
|
if ( result ) {
|
|
XP_U8 loc = board->dividerLoc[board->selPlayer];
|
|
loc += MAX_TRAY_TILES + 1;
|
|
loc += right? 1:-1;
|
|
loc %= MAX_TRAY_TILES + 1;
|
|
|
|
dividerMoved( board, loc );
|
|
}
|
|
return result;
|
|
} /* board_moveDivider */
|
|
#endif
|
|
|
|
#ifdef CPLUS
|
|
}
|
|
#endif
|