2008-07-18 09:49:00 +02:00
|
|
|
/* -*-mode: C; fill-column: 78; compile-command: "cd ../linux && make MEMDEBUG=TRUE"; -*- */
|
2008-03-09 00:19:04 +01:00
|
|
|
/*
|
|
|
|
* Copyright 1997 - 2008 by Eric House (xwords@eehouse.org). All rights
|
|
|
|
* reserved.
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef CPLUS
|
|
|
|
extern "C" {
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "dragdrpp.h"
|
2008-03-13 13:23:22 +01:00
|
|
|
#include "game.h"
|
2008-03-09 00:19:04 +01:00
|
|
|
|
2008-04-08 05:55:53 +02:00
|
|
|
/* How many squares must scroll gesture take in to be recognized. */
|
|
|
|
#define SCROLL_DRAG_THRESHHOLD 3
|
|
|
|
|
2008-03-09 00:19:04 +01:00
|
|
|
static XP_Bool dragDropContinueImpl( BoardCtxt* board, XP_U16 xx, XP_U16 yy,
|
|
|
|
BoardObjectType* onWhichP );
|
2008-03-11 13:22:10 +01:00
|
|
|
static void invalDragObjRange( BoardCtxt* board, const DragObjInfo* from,
|
|
|
|
const DragObjInfo* to );
|
2008-05-26 02:27:48 +02:00
|
|
|
#ifdef XWFEATURE_SEARCHLIMIT
|
2008-03-13 13:23:22 +01:00
|
|
|
static void invalHintRectDiffs( BoardCtxt* board, const DragObjInfo* cur,
|
|
|
|
const DragObjInfo* nxt );
|
|
|
|
static void setLimitsFrom( const BoardCtxt* board, BdHintLimits* limits );
|
2008-05-26 02:27:48 +02:00
|
|
|
#endif
|
2008-03-09 00:19:04 +01:00
|
|
|
|
2008-03-29 21:23:27 +01:00
|
|
|
static void startScrollTimerIf( BoardCtxt* board );
|
|
|
|
|
2008-03-09 00:19:04 +01:00
|
|
|
XP_Bool
|
|
|
|
dragDropInProgress( const BoardCtxt* board )
|
|
|
|
{
|
|
|
|
const DragState* ds = &board->dragState;
|
|
|
|
/* LOG_RETURNF( "%d", ds->dragInProgress ); */
|
2008-03-13 13:23:22 +01:00
|
|
|
return ds->dtype != DT_NONE;
|
|
|
|
} /* dragDropInProgress */
|
|
|
|
|
|
|
|
XP_Bool
|
|
|
|
dragDropHasMoved( const BoardCtxt* board )
|
|
|
|
{
|
|
|
|
return dragDropInProgress(board) && board->dragState.didMove;
|
|
|
|
} /* dragDropHasMoved */
|
2008-03-09 00:19:04 +01:00
|
|
|
|
|
|
|
static XP_Bool
|
2008-03-13 13:23:22 +01:00
|
|
|
ddStartBoard( BoardCtxt* board, XP_U16 xx, XP_U16 yy )
|
2008-03-09 00:19:04 +01:00
|
|
|
{
|
|
|
|
DragState* ds = &board->dragState;
|
2008-03-13 13:23:22 +01:00
|
|
|
XP_Bool found;
|
2008-04-08 05:55:53 +02:00
|
|
|
XP_Bool trayVisible;
|
2008-03-13 13:23:22 +01:00
|
|
|
XP_U16 col, row;
|
2008-03-09 00:19:04 +01:00
|
|
|
|
2008-03-13 13:23:22 +01:00
|
|
|
found = coordToCell( board, xx, yy, &col, &row );
|
|
|
|
XP_ASSERT( found );
|
2008-03-09 00:19:04 +01:00
|
|
|
|
2008-04-08 05:55:53 +02:00
|
|
|
trayVisible = board->trayVisState == TRAY_REVEALED;
|
|
|
|
if ( trayVisible && holdsPendingTile( board, col, row ) ) {
|
2008-03-13 13:23:22 +01:00
|
|
|
XP_U16 modelc, modelr;
|
|
|
|
XP_Bool ignore;
|
2008-03-09 00:19:04 +01:00
|
|
|
|
2008-03-13 13:23:22 +01:00
|
|
|
ds->dtype = DT_TILE;
|
|
|
|
flipIf( board, col, row, &modelc, &modelr );
|
|
|
|
|
|
|
|
found = model_getTile( board->model, modelc, modelr, XP_TRUE,
|
|
|
|
board->selPlayer, &ds->tile, &ds->isBlank,
|
|
|
|
&ignore, &ignore );
|
2008-04-08 05:55:53 +02:00
|
|
|
XP_ASSERT( found );
|
|
|
|
} else {
|
|
|
|
/* If we're not dragging a tile, we can either drag the board (scroll)
|
|
|
|
or work on hint regions. Sometimes scrolling isn't possible.
|
|
|
|
Sometimes hint dragging is disabled. But if both are possible,
|
|
|
|
then the alt key determines it. I figure scrolling will be more
|
|
|
|
common than hint dragging when both are possible, but you can turn
|
|
|
|
hint dragging off, so if it's on that's probably what you want. */
|
|
|
|
XP_Bool canScroll = board->lastVisibleRow < model_numRows(board->model);
|
|
|
|
if ( 0 ) {
|
2008-03-13 13:23:22 +01:00
|
|
|
#ifdef XWFEATURE_SEARCHLIMIT
|
2008-12-01 14:06:37 +01:00
|
|
|
} else if ( !board->gi->hintsNotAllowed && board->gi->allowHintRect
|
|
|
|
&& trayVisible ) {
|
2008-04-08 05:55:53 +02:00
|
|
|
if ( !util_altKeyDown(board->util) ) {
|
|
|
|
ds->dtype = DT_HINTRGN;
|
|
|
|
} else if ( canScroll ) {
|
|
|
|
ds->dtype = DT_BOARD;
|
|
|
|
}
|
2008-03-13 13:23:22 +01:00
|
|
|
#endif
|
2008-04-08 05:55:53 +02:00
|
|
|
} else if ( canScroll ) {
|
|
|
|
ds->dtype = DT_BOARD;
|
|
|
|
}
|
2008-03-13 13:23:22 +01:00
|
|
|
}
|
|
|
|
ds->start.u.board.col = col;
|
|
|
|
ds->start.u.board.row = row;
|
2008-03-09 00:19:04 +01:00
|
|
|
|
2008-03-13 13:23:22 +01:00
|
|
|
return ds->dtype != DT_NONE;
|
2008-03-09 00:19:04 +01:00
|
|
|
} /* ddStartBoard */
|
|
|
|
|
|
|
|
static XP_Bool
|
|
|
|
ddStartTray( BoardCtxt* board, XP_U16 x, XP_U16 y )
|
|
|
|
{
|
|
|
|
XP_Bool canDrag;
|
|
|
|
DragState* ds = &board->dragState;
|
|
|
|
|
|
|
|
XP_Bool onDivider;
|
|
|
|
XP_S16 index = pointToTileIndex( board, x, y, &onDivider );
|
|
|
|
canDrag = onDivider || index >= 0;
|
|
|
|
if ( canDrag ) {
|
|
|
|
if ( onDivider ) {
|
2008-03-10 01:49:06 +01:00
|
|
|
board->dividerInvalid = XP_TRUE;
|
2008-07-18 09:49:00 +02:00
|
|
|
ds->start.u.tray.index = board->selInfo->dividerLoc;
|
2008-03-13 13:23:22 +01:00
|
|
|
|
|
|
|
ds->dtype = DT_DIVIDER;
|
2008-03-09 00:19:04 +01:00
|
|
|
} else {
|
|
|
|
Tile tile;
|
2008-07-18 09:49:00 +02:00
|
|
|
tile = model_getPlayerTile( board->model, board->selPlayer, index );
|
2008-03-09 00:19:04 +01:00
|
|
|
ds->isBlank =
|
|
|
|
tile == dict_getBlankTile( model_getDictionary(board->model) );
|
|
|
|
ds->tile = tile;
|
|
|
|
|
|
|
|
ds->start.u.tray.index = index;
|
|
|
|
|
|
|
|
/* during drag the moving tile is drawn as selected, so inval
|
|
|
|
currently selected tile. */
|
2008-07-18 09:49:00 +02:00
|
|
|
board_invalTrayTiles( board, board->selInfo->traySelBits );
|
2008-03-13 13:23:22 +01:00
|
|
|
|
|
|
|
ds->dtype = DT_TILE;
|
2008-03-09 00:19:04 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return canDrag;
|
|
|
|
} /* ddStartTray */
|
|
|
|
|
|
|
|
/* x and y, in board coordinates (not model, should the board be flipped), are
|
|
|
|
* already col,row (cells) in the board case, but real x/y (pixels) on the
|
|
|
|
* tray.
|
|
|
|
*/
|
|
|
|
XP_Bool
|
|
|
|
dragDropStart( BoardCtxt* board, BoardObjectType obj, XP_U16 x, XP_U16 y )
|
|
|
|
{
|
|
|
|
XP_Bool result = XP_FALSE;
|
|
|
|
DragState* ds = &board->dragState;
|
2008-06-08 01:05:54 +02:00
|
|
|
if ( dragDropInProgress(board) ) {
|
|
|
|
XP_LOGF( "warning: starting drag while dragDropInProgress() true" );
|
|
|
|
}
|
2008-03-09 00:19:04 +01:00
|
|
|
XP_MEMSET( ds, 0, sizeof(*ds) );
|
|
|
|
|
|
|
|
ds->start.obj = obj;
|
|
|
|
|
|
|
|
if ( OBJ_BOARD == obj ) {
|
|
|
|
result = ddStartBoard( board, x, y );
|
|
|
|
} else if ( OBJ_TRAY == obj ) {
|
|
|
|
result = ddStartTray( board, x, y );
|
|
|
|
} else {
|
|
|
|
XP_ASSERT(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( result ) {
|
|
|
|
ds->cur = ds->start;
|
|
|
|
invalDragObj( board, &ds->start );
|
2008-03-29 21:23:27 +01:00
|
|
|
startScrollTimerIf( board );
|
2008-03-09 00:19:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
} /* dragDropStart */
|
|
|
|
|
|
|
|
/* We're potentially dragging. If we're leaving one object, inval it.
|
|
|
|
* If we're entering another, inval it. Track where we are so we don't inval
|
|
|
|
* again. If we didn't really move, don't inval anything!
|
|
|
|
*
|
|
|
|
* Our overriding concern must be to preserve data integrity in the face of
|
|
|
|
* buggy OSes that may drop events (esp. MouseUp). So never own a tile: keep
|
|
|
|
* it attached either to the tray or the board. If the state of the board is
|
|
|
|
* to change as dragging happens, that's for the board (view) to display, not
|
|
|
|
* the model. So drawing has to be aware of drag-and-drop, but we don't
|
|
|
|
* change the model until the pen's actually lifted. If we never get the
|
|
|
|
* pen-up event at the worst the board's drawn incorrectly for a bit.
|
|
|
|
*
|
|
|
|
* Exception: since divider location is in the board rather than the model
|
|
|
|
* just change it every time we can.
|
|
|
|
*/
|
|
|
|
XP_Bool
|
|
|
|
dragDropContinue( BoardCtxt* board, XP_U16 xx, XP_U16 yy )
|
|
|
|
{
|
|
|
|
BoardObjectType ignore;
|
|
|
|
XP_ASSERT( dragDropInProgress(board) );
|
|
|
|
|
|
|
|
return dragDropContinueImpl( board, xx, yy, &ignore );
|
|
|
|
}
|
|
|
|
|
|
|
|
XP_Bool
|
|
|
|
dragDropEnd( BoardCtxt* board, XP_U16 xx, XP_U16 yy, XP_Bool* dragged )
|
|
|
|
{
|
|
|
|
DragState* ds = &board->dragState;
|
|
|
|
BoardObjectType newObj;
|
|
|
|
|
|
|
|
XP_ASSERT( dragDropInProgress(board) );
|
|
|
|
|
|
|
|
(void)dragDropContinueImpl( board, xx, yy, &newObj );
|
|
|
|
*dragged = ds->didMove;
|
|
|
|
|
|
|
|
/* If we've dropped on something, put the tile there! Since we
|
|
|
|
don't remove it from its earlier location until it's dropped,
|
|
|
|
we need to specify where it's coming from. */
|
2008-03-13 13:23:22 +01:00
|
|
|
if ( ds->dtype == DT_DIVIDER ) {
|
2008-03-09 00:19:04 +01:00
|
|
|
board->dividerInvalid = XP_TRUE;
|
|
|
|
/* nothing to do */
|
2008-03-13 13:23:22 +01:00
|
|
|
#ifdef XWFEATURE_SEARCHLIMIT
|
|
|
|
} else if ( ds->dtype == DT_HINTRGN ) {
|
|
|
|
if ( OBJ_BOARD == newObj && ds->didMove ) {
|
|
|
|
XP_Bool makeActive = ds->start.u.board.row <= ds->cur.u.board.row;
|
2008-07-18 09:49:00 +02:00
|
|
|
board->selInfo->hasHintRect = makeActive;
|
2008-03-13 13:23:22 +01:00
|
|
|
if ( makeActive ) {
|
2008-07-18 09:49:00 +02:00
|
|
|
setLimitsFrom( board, &board->selInfo->limits );
|
2008-03-13 13:23:22 +01:00
|
|
|
} else {
|
|
|
|
invalHintRectDiffs( board, &ds->cur, NULL );
|
|
|
|
}
|
|
|
|
board_resetEngine( board );
|
|
|
|
} else {
|
|
|
|
/* return it to whatever it was */
|
|
|
|
invalHintRectDiffs( board, &ds->cur, NULL );
|
|
|
|
invalCurHintRect( board, board->selPlayer );
|
|
|
|
}
|
|
|
|
#endif
|
2008-06-08 01:05:54 +02:00
|
|
|
} else if ( ds->dtype == DT_BOARD ) {
|
|
|
|
/* do nothing */
|
2008-03-09 00:19:04 +01:00
|
|
|
} else {
|
|
|
|
XP_U16 mod_startc, mod_startr;
|
|
|
|
|
|
|
|
flipIf( board, ds->start.u.board.col, ds->start.u.board.row,
|
|
|
|
&mod_startc, &mod_startr );
|
|
|
|
|
|
|
|
if ( newObj == OBJ_TRAY ) {
|
|
|
|
if ( ds->start.obj == OBJ_BOARD ) { /* board->tray is pending */
|
|
|
|
model_moveBoardToTray( board->model, board->selPlayer,
|
|
|
|
mod_startc, mod_startr,
|
|
|
|
ds->cur.u.tray.index );
|
|
|
|
} else {
|
|
|
|
model_moveTileOnTray( board->model, board->selPlayer,
|
|
|
|
ds->start.u.tray.index,
|
|
|
|
ds->cur.u.tray.index );
|
|
|
|
}
|
2008-03-29 21:23:27 +01:00
|
|
|
} else if ( (newObj == OBJ_BOARD) &&
|
|
|
|
!cellOccupied( board, ds->cur.u.board.col,
|
|
|
|
ds->cur.u.board.row, XP_TRUE ) ) {
|
|
|
|
if ( ds->start.obj == OBJ_TRAY ) {
|
|
|
|
/* moveTileToBoard flips its inputs */
|
|
|
|
(void)moveTileToBoard( board, ds->cur.u.board.col,
|
|
|
|
ds->cur.u.board.row,
|
|
|
|
ds->start.u.tray.index, EMPTY_TILE );
|
|
|
|
} else if ( ds->start.obj == OBJ_BOARD ) {
|
|
|
|
XP_U16 mod_curc, mod_curr;
|
|
|
|
flipIf( board, ds->cur.u.board.col, ds->cur.u.board.row,
|
|
|
|
&mod_curc, &mod_curr );
|
2008-06-08 01:05:54 +02:00
|
|
|
if ( model_moveTileOnBoard( board->model, board->selPlayer,
|
|
|
|
mod_startc, mod_startr, mod_curc,
|
|
|
|
mod_curr ) ) {
|
|
|
|
/* inval points tile in case score changed */
|
|
|
|
board_invalTrayTiles( board, 1 << (MAX_TRAY_TILES-1) );
|
|
|
|
}
|
2008-03-09 00:19:04 +01:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* We're returning it to start, so will be re-inserted in tray */
|
|
|
|
if ( OBJ_TRAY == ds->start.obj ) {
|
|
|
|
invalTrayTilesAbove( board, ds->start.u.tray.index );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* These may change appearance, e.g. from big tile to dropped cell. */
|
|
|
|
invalDragObj( board, &ds->cur );
|
|
|
|
invalDragObj( board, &ds->start );
|
|
|
|
}
|
2008-03-13 13:23:22 +01:00
|
|
|
ds->dtype = DT_NONE;
|
2008-03-09 00:19:04 +01:00
|
|
|
|
|
|
|
return XP_TRUE;
|
|
|
|
} /* dragDropEnd */
|
|
|
|
|
|
|
|
XP_Bool
|
|
|
|
dragDropGetBoardTile( const BoardCtxt* board, XP_U16* col, XP_U16* row )
|
|
|
|
{
|
|
|
|
const DragState* ds = &board->dragState;
|
2008-03-13 13:23:22 +01:00
|
|
|
XP_Bool found = ds->dtype == DT_TILE && ds->cur.obj == OBJ_BOARD;
|
2008-03-09 00:19:04 +01:00
|
|
|
if ( found ) {
|
|
|
|
*col = ds->cur.u.board.col;
|
|
|
|
*row = ds->cur.u.board.row;
|
|
|
|
}
|
|
|
|
return found;
|
|
|
|
}
|
|
|
|
|
|
|
|
XP_Bool
|
|
|
|
dragDropIsBeingDragged( const BoardCtxt* board, XP_U16 col, XP_U16 row,
|
|
|
|
XP_Bool* isOrigin )
|
|
|
|
{
|
|
|
|
const DragState* ds = &board->dragState;
|
2008-03-13 13:23:22 +01:00
|
|
|
XP_Bool result = ds->dtype == DT_TILE && ds->cur.obj == OBJ_BOARD;
|
2008-03-09 00:19:04 +01:00
|
|
|
if ( result ) {
|
|
|
|
const DragState* ds = &board->dragState;
|
|
|
|
if ( (ds->cur.obj == OBJ_BOARD) && (ds->cur.u.board.col == col)
|
|
|
|
&& (ds->cur.u.board.row == row) ) {
|
|
|
|
*isOrigin = XP_FALSE;
|
|
|
|
} else if ( (ds->start.obj == OBJ_BOARD)
|
|
|
|
&& (ds->start.u.board.col == col)
|
|
|
|
&& (ds->start.u.board.row == row) ) {
|
|
|
|
*isOrigin = XP_TRUE;
|
|
|
|
} else {
|
|
|
|
result = XP_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
dragDropGetTrayChanges( const BoardCtxt* board, XP_U16* rmvdIndx,
|
|
|
|
XP_U16* addedIndx )
|
|
|
|
{
|
|
|
|
const DragState* ds = &board->dragState;
|
|
|
|
*addedIndx = *rmvdIndx = MAX_TRAY_TILES; /* too big means ignore me */
|
2008-03-13 13:23:22 +01:00
|
|
|
if ( ds->dtype == DT_TILE ) {
|
2008-03-09 00:19:04 +01:00
|
|
|
if ( OBJ_TRAY == ds->start.obj ) {
|
|
|
|
*rmvdIndx = ds->start.u.tray.index;
|
|
|
|
}
|
|
|
|
if ( OBJ_TRAY == ds->cur.obj ) {
|
|
|
|
*addedIndx = ds->cur.u.tray.index;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-05-26 02:27:48 +02:00
|
|
|
#ifdef XWFEATURE_SEARCHLIMIT
|
2008-03-13 13:23:22 +01:00
|
|
|
XP_Bool
|
|
|
|
dragDropGetHintLimits( const BoardCtxt* board, BdHintLimits* limits )
|
|
|
|
{
|
|
|
|
XP_Bool result = board->dragState.dtype == DT_HINTRGN;
|
|
|
|
if ( result ) {
|
|
|
|
setLimitsFrom( board, limits );
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
2008-05-26 02:27:48 +02:00
|
|
|
#endif
|
2008-03-13 13:23:22 +01:00
|
|
|
|
2008-03-09 00:19:04 +01:00
|
|
|
XP_Bool
|
|
|
|
dragDropIsDividerDrag( const BoardCtxt* board )
|
|
|
|
{
|
2008-03-13 13:23:22 +01:00
|
|
|
return board->dragState.dtype == DT_DIVIDER;
|
2008-03-09 00:19:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
dragDropTileInfo( const BoardCtxt* board, Tile* tile, XP_Bool* isBlank )
|
|
|
|
{
|
|
|
|
const DragState* ds = &board->dragState;
|
|
|
|
XP_ASSERT( dragDropInProgress( board ) );
|
2008-03-13 13:23:22 +01:00
|
|
|
XP_ASSERT( ds->dtype == DT_TILE );
|
2008-03-09 00:19:04 +01:00
|
|
|
XP_ASSERT ( OBJ_BOARD == ds->start.obj || OBJ_TRAY == ds->start.obj );
|
|
|
|
*tile = ds->tile;
|
|
|
|
*isBlank = ds->isBlank;
|
|
|
|
}
|
|
|
|
|
2008-05-26 02:27:48 +02:00
|
|
|
#ifdef XWFEATURE_SEARCHLIMIT
|
2008-03-13 13:23:22 +01:00
|
|
|
static void
|
|
|
|
invalHintRectDiffs( BoardCtxt* board, const DragObjInfo* cur,
|
|
|
|
const DragObjInfo* nxt )
|
|
|
|
{
|
|
|
|
XP_U16 startCol = board->dragState.start.u.board.col;
|
|
|
|
XP_U16 startRow = board->dragState.start.u.board.row;
|
|
|
|
|
|
|
|
/* These two regions will generally have close to 50% of their borders in
|
|
|
|
common. Try not to inval what needn't be inval'd. But at the moment
|
|
|
|
performance seems good enough without adding the complexity and new
|
|
|
|
bugs...
|
|
|
|
|
|
|
|
The challenge in doing a smarter diff is that some squares need to be
|
|
|
|
invalidated even if they're part of the borders of both limits rects,
|
|
|
|
in particular if one is a corner of one and just a side of another.
|
|
|
|
One simple but expensive way of accounting for this would be to call
|
|
|
|
figureHintAtts() on each square in the borders of both rects and
|
|
|
|
invalidate when the hintAttributes aren't the same for both. That
|
|
|
|
misses an opportunity to avoid doing any calculations on those border
|
|
|
|
squares that clearly haven't changed at all.
|
|
|
|
*/
|
|
|
|
|
|
|
|
invalCellRegion( board, startCol, startRow, cur->u.board.col,
|
|
|
|
cur->u.board.row );
|
|
|
|
if ( !!nxt ) {
|
|
|
|
BdHintLimits limits;
|
|
|
|
setLimitsFrom( board, &limits );
|
|
|
|
invalCellRegion( board, startCol, startRow, nxt->u.board.col,
|
|
|
|
nxt->u.board.row );
|
|
|
|
}
|
2008-05-26 02:27:48 +02:00
|
|
|
} /* invalHintRectDiffs */
|
|
|
|
#endif
|
2008-03-13 13:23:22 +01:00
|
|
|
|
2008-03-09 00:19:04 +01:00
|
|
|
static XP_Bool
|
|
|
|
dragDropContinueImpl( BoardCtxt* board, XP_U16 xx, XP_U16 yy,
|
|
|
|
BoardObjectType* onWhichP )
|
|
|
|
{
|
|
|
|
XP_Bool moving = XP_FALSE;
|
|
|
|
DragObjInfo newInfo;
|
|
|
|
DragState* ds = &board->dragState;
|
|
|
|
|
|
|
|
if ( !pointOnSomething( board, xx, yy, &newInfo.obj ) ) {
|
|
|
|
newInfo.obj = OBJ_NONE;
|
|
|
|
}
|
|
|
|
*onWhichP = newInfo.obj;
|
|
|
|
|
2008-04-08 05:55:53 +02:00
|
|
|
if ( newInfo.obj == OBJ_BOARD ) {
|
|
|
|
(void)coordToCell( board, xx, yy, &newInfo.u.board.col,
|
|
|
|
&newInfo.u.board.row );
|
|
|
|
}
|
|
|
|
|
2008-03-13 13:23:22 +01:00
|
|
|
if ( ds->dtype == DT_DIVIDER ) {
|
2008-03-10 01:49:06 +01:00
|
|
|
if ( OBJ_TRAY == newInfo.obj ) {
|
2008-03-09 00:19:04 +01:00
|
|
|
XP_U16 newloc;
|
|
|
|
XP_U16 scale = board->trayScaleH;
|
|
|
|
xx -= board->trayBounds.left;
|
|
|
|
newloc = xx / scale;
|
2008-04-08 05:55:53 +02:00
|
|
|
if ( (xx % scale) > ((scale+board->dividerWidth)/2)) {
|
2008-03-09 00:19:04 +01:00
|
|
|
++newloc;
|
|
|
|
}
|
|
|
|
moving = dividerMoved( board, newloc );
|
|
|
|
}
|
2008-03-13 13:23:22 +01:00
|
|
|
#ifdef XWFEATURE_SEARCHLIMIT
|
2008-03-29 21:23:27 +01:00
|
|
|
} else if ( ds->dtype == DT_HINTRGN && newInfo.obj != OBJ_BOARD ) {
|
2008-03-13 13:23:22 +01:00
|
|
|
/* do nothing */
|
|
|
|
#endif
|
2008-04-08 05:55:53 +02:00
|
|
|
} else if ( ds->dtype == DT_BOARD ) {
|
|
|
|
if ( newInfo.obj == OBJ_BOARD ) {
|
|
|
|
XP_S16 diff = newInfo.u.board.row - ds->cur.u.board.row;
|
|
|
|
diff /= SCROLL_DRAG_THRESHHOLD;
|
|
|
|
moving = adjustYOffset( board, diff );
|
|
|
|
}
|
2008-03-29 21:23:27 +01:00
|
|
|
} else {
|
|
|
|
if ( newInfo.obj == OBJ_BOARD ) {
|
2008-04-08 05:55:53 +02:00
|
|
|
moving = (newInfo.u.board.col != ds->cur.u.board.col)
|
2008-03-29 21:23:27 +01:00
|
|
|
|| (newInfo.u.board.row != ds->cur.u.board.row)
|
|
|
|
|| (OBJ_TRAY == ds->cur.obj);
|
|
|
|
} else if ( newInfo.obj == OBJ_TRAY ) {
|
|
|
|
XP_Bool onDivider;
|
|
|
|
XP_S16 index = pointToTileIndex( board, xx, yy, &onDivider );
|
|
|
|
if ( !onDivider ) {
|
|
|
|
if ( index < 0 ) { /* negative means onto empty part of
|
|
|
|
tray. Force left. */
|
|
|
|
index = model_getNumTilesInTray( board->model,
|
|
|
|
board->selPlayer );
|
|
|
|
if ( OBJ_TRAY == ds->start.obj ) {
|
|
|
|
--index; /* dragging right into space */
|
2008-03-13 13:23:22 +01:00
|
|
|
}
|
2008-03-09 00:19:04 +01:00
|
|
|
}
|
2008-03-29 21:23:27 +01:00
|
|
|
moving = (OBJ_BOARD == ds->cur.obj)
|
|
|
|
|| (index != ds->cur.u.tray.index);
|
|
|
|
if ( moving ) {
|
|
|
|
newInfo.u.tray.index = index;
|
|
|
|
}
|
2008-03-09 00:19:04 +01:00
|
|
|
}
|
2008-03-29 21:23:27 +01:00
|
|
|
}
|
2008-03-09 00:19:04 +01:00
|
|
|
|
2008-03-29 21:23:27 +01:00
|
|
|
if ( moving ) {
|
|
|
|
if ( ds->dtype == DT_TILE ) {
|
|
|
|
invalDragObjRange( board, &ds->cur, &newInfo );
|
2008-03-13 13:23:22 +01:00
|
|
|
#ifdef XWFEATURE_SEARCHLIMIT
|
2008-03-29 21:23:27 +01:00
|
|
|
} else if ( ds->dtype == DT_HINTRGN ) {
|
|
|
|
invalHintRectDiffs( board, &ds->cur, &newInfo );
|
|
|
|
if ( !ds->didMove ) { /* first time through */
|
|
|
|
invalCurHintRect( board, board->selPlayer );
|
|
|
|
}
|
2008-03-13 13:23:22 +01:00
|
|
|
#endif
|
|
|
|
}
|
2008-03-29 21:23:27 +01:00
|
|
|
|
|
|
|
XP_MEMCPY( &ds->cur, &newInfo, sizeof(ds->cur) );
|
|
|
|
startScrollTimerIf( board );
|
2008-03-09 00:19:04 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( moving ) {
|
2008-03-13 13:23:22 +01:00
|
|
|
if ( !ds->didMove ) {
|
|
|
|
/* This is the first time we've moved!!! Kill any future timers,
|
|
|
|
and if there's a window up kill it.*/
|
|
|
|
board->penTimerFired = XP_FALSE;
|
|
|
|
if ( valHintMiniWindowActive( board ) ) {
|
|
|
|
hideMiniWindow( board, XP_TRUE, MINIWINDOW_VALHINT );
|
|
|
|
}
|
|
|
|
}
|
2008-03-09 00:19:04 +01:00
|
|
|
ds->didMove = XP_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return moving;
|
|
|
|
} /* dragDropContinueImpl */
|
|
|
|
|
2008-03-11 13:22:10 +01:00
|
|
|
static 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 );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-05-26 02:27:48 +02:00
|
|
|
#ifdef XWFEATURE_SEARCHLIMIT
|
2008-03-13 13:23:22 +01:00
|
|
|
static void
|
|
|
|
setLimitsFrom( const BoardCtxt* board, BdHintLimits* limits )
|
|
|
|
{
|
|
|
|
const DragState* ds = &board->dragState;
|
|
|
|
limits->left = XP_MIN( ds->start.u.board.col, ds->cur.u.board.col );
|
|
|
|
limits->right = XP_MAX( ds->start.u.board.col, ds->cur.u.board.col );
|
|
|
|
limits->top = XP_MIN( ds->start.u.board.row, ds->cur.u.board.row );
|
|
|
|
limits->bottom = XP_MAX( ds->start.u.board.row, ds->cur.u.board.row );
|
|
|
|
}
|
2008-05-26 02:27:48 +02:00
|
|
|
#endif
|
2008-03-13 13:23:22 +01:00
|
|
|
|
2008-04-12 17:36:31 +02:00
|
|
|
static XP_Bool
|
2008-12-06 16:59:30 +01:00
|
|
|
scrollTimerProc( void* closure, XWTimerReason XP_UNUSED_DBG(why) )
|
2008-03-29 21:23:27 +01:00
|
|
|
{
|
2008-04-12 17:36:31 +02:00
|
|
|
XP_Bool draw = XP_FALSE;
|
2008-03-29 21:23:27 +01:00
|
|
|
BoardCtxt* board = (BoardCtxt*)closure;
|
|
|
|
DragState* ds = &board->dragState;
|
|
|
|
XP_ASSERT( why == TIMER_PENDOWN );
|
|
|
|
|
|
|
|
if ( ds->scrollTimerSet ) {
|
|
|
|
XP_S16 change;
|
|
|
|
ds->scrollTimerSet = XP_FALSE;
|
|
|
|
if ( onBorderCanScroll( board, ds->cur.u.board.row, &change ) ) {
|
|
|
|
invalDragObj( board, &ds->cur );
|
|
|
|
ds->cur.u.board.row += (change >0 ? 1 : -1);
|
|
|
|
if ( checkScrollCell( board, ds->cur.u.board.col,
|
|
|
|
ds->cur.u.board.row ) ) {
|
|
|
|
board_draw( board ); /* may fail, e.g. on wince */
|
|
|
|
startScrollTimerIf( board );
|
2008-04-12 17:36:31 +02:00
|
|
|
draw = XP_TRUE;
|
2008-03-29 21:23:27 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2008-04-12 17:36:31 +02:00
|
|
|
return draw;
|
2008-03-29 21:23:27 +01:00
|
|
|
} /* scrollTimerProc */
|
|
|
|
|
|
|
|
static void
|
|
|
|
startScrollTimerIf( BoardCtxt* board )
|
|
|
|
{
|
|
|
|
DragState* ds = &board->dragState;
|
|
|
|
|
2008-04-08 05:55:53 +02:00
|
|
|
if ( (ds->dtype == DT_TILE) && (ds->cur.obj == OBJ_BOARD) ) {
|
2008-03-29 21:23:27 +01:00
|
|
|
XP_S16 ignore;
|
|
|
|
if ( onBorderCanScroll( board, ds->cur.u.board.row, &ignore ) ) {
|
|
|
|
util_setTimer( board->util, TIMER_PENDOWN, 0,
|
|
|
|
scrollTimerProc, (void*) board );
|
|
|
|
ds->scrollTimerSet = XP_TRUE;
|
|
|
|
} else {
|
|
|
|
/* ignore if we've moved off */
|
|
|
|
ds->scrollTimerSet = XP_FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} /* startScrollTimerIf */
|
|
|
|
|
2008-03-09 00:19:04 +01:00
|
|
|
#ifdef CPLUS
|
|
|
|
}
|
|
|
|
#endif
|