1 /***************************************************************************
2     File                 : board.cpp
3     Project              : Knights
4     Description          : Game board (scene)
5     --------------------------------------------------------------------
6     Copyright            : (C) 2016 by Alexander Semke (alexander.semke@web.de)
7     Copyright            : (C) 2009-2011 by Miha Čančula (miha@noughmad.eu)
8 
9  ***************************************************************************/
10 
11 /***************************************************************************
12  *                                                                         *
13  *  This program is free software; you can redistribute it and/or modify   *
14  *  it under the terms of the GNU General Public License as published by   *
15  *  the Free Software Foundation; either version 2 of the License, or      *
16  *  (at your option) any later version.                                    *
17  *                                                                         *
18  *  This program is distributed in the hope that it will be useful,        *
19  *  but WITHOUT ANY WARRANTY; without even the implied warranty of         *
20  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
21  *  GNU General Public License for more details.                           *
22  *                                                                         *
23  *   You should have received a copy of the GNU General Public License     *
24  *   along with this program; if not, write to the Free Software           *
25  *   Foundation, Inc., 51 Franklin Street, Fifth Floor,                    *
26  *   Boston, MA  02110-1301  USA                                           *
27  *                                                                         *
28  ***************************************************************************/
29 
30 #include "board.h"
31 #include "gamemanager.h"
32 #include "settings.h"
33 #include "core/pos.h"
34 #include "core/move.h"
35 #include "core/item.h"
36 #include "rules/chessrules.h"
37 #include "ui_promotiondialog.h"
38 
39 #include <KGameRenderer>
40 
41 #include <QDialog>
42 #include <QDialogButtonBox>
43 #include <QDrag>
44 #include <QGraphicsSceneMouseEvent>
45 #include <QMimeData>
46 #include <QPushButton>
47 #include <QtMath>
48 
49 using namespace Knights;
50 
51 const qreal backgroundZValue = -3.0;
52 const qreal borderZValue = -2.0;
53 const qreal notationZValue = -1.0;
54 const qreal tileZValue = 0.0;
55 const qreal pieceZValue = 1.0;
56 const qreal legalMarkerZValue = 3.0;
57 const qreal dragZValue = 4.0;
58 
59 const QString backgroundKey = QStringLiteral ( "Background" );
60 const QString whiteTileKey = QStringLiteral ( "WhiteTile" );
61 const QString blackTileKey = QStringLiteral ( "BlackTile" );
62 const QString legalMarkerKey = QStringLiteral ( "Marker" );
63 const QString motionMarkerKey = QStringLiteral ( "Motion" );
64 const QString dangerMarkerKey = QStringLiteral ( "Danger" );
65 
66 const QString tbBorderKey = QStringLiteral ( "TopBottomBorder" );
67 const QString lrBorderKey = QStringLiteral ( "LeftRightBorder" );
68 const QString whiteLettersKey = QStringLiteral ( "WhiteLetters" );
69 const QString blackLettersKey = QStringLiteral ( "BlackLetters" );
70 const QString whiteNumbersKey = QStringLiteral ( "WhiteNumbers" );
71 const QString blackNumbersKey = QStringLiteral ( "BlackNumbers" );
72 
Board(KgThemeProvider * provider,QObject * parent)73 Board::Board(KgThemeProvider* provider, QObject* parent) : QGraphicsScene(parent),
74 	m_rules(nullptr),
75 	m_background(nullptr),
76 	m_displayBorders(true),
77 	m_displayNotations(true),
78 	renderer(nullptr),
79 	m_themeProvider(provider),
80 	m_dragActive(false),
81 	draggedPiece(nullptr),
82 	selectedPiece(nullptr),
83 	m_paused(false),
84 	m_animated(true),
85 	m_currentPlayer(White),
86 	m_displayedPlayer(NoColor),
87 	m_drawFrame(true) {
88 
89 	renderer = new KGameRenderer(m_themeProvider);
90 	if (!Manager::self()->rules())
91 		Manager::self()->setRules(new ChessRules);
92 	Manager::self()->rules()->setGrid(&m_grid);
93 	updateTheme();
94 
95 	connect (provider, &KgThemeProvider::currentThemeChanged, this, &Board::updateTheme);
96 }
97 
~Board()98 Board::~Board() {
99 	qDeleteAll(m_grid);
100 	qDeleteAll(m_tiles);
101 	qDeleteAll(markers);
102 	delete renderer;
103 	//TODO: this crashes the application, s.a. BUG 405763
104 	//delete Manager::self()->rules();
105 }
106 
addPiece(PieceType type,Color color,const Pos & pos)107 void Board::addPiece(PieceType type, Color color, const Pos& pos) {
108 	Piece* t_piece = new Piece ( renderer, type, color, this, pos );
109 	if ( Settings::animationSpeed() != Settings::EnumAnimationSpeed::Instant )
110 		t_piece->setPos ( mapToScene ( Pos ( ( pos.first > 4 ) ? 5 : 4, ( pos.second > 4 ) ? 5 : 4 ) ) );
111 	t_piece->setZValue ( pieceZValue );
112 	m_grid.insert ( pos, t_piece );
113 }
114 
movePiece(const Move & move)115 void Board::movePiece(const Move& move) {
116 	qCDebug(LOG_KNIGHTS) << move;
117 	Move m = move;
118 	if ( ( m.flag ( Move::Illegal ) && !m.flag ( Move::Forced ) ) ||  m.to() == m.from() || !m_grid.contains ( m.from() ) ) {
119 		qCWarning(LOG_KNIGHTS) << "Invalid move:" << m;
120 		return;
121 	}
122 	if ( !m.flag ( Move::Forced ) &&
123 	        ( m_grid[m.from()]->color() != m_currentPlayer || !Manager::self()->rules()->legalMoves(m.from()).contains(m) ) ) {
124 		qCWarning(LOG_KNIGHTS) << "Move not allowed:" << m;
125 		return;
126 	}
127 	qDeleteAll ( markers );
128 	markers.clear();
129 	if ( m.flag(Move::Promote) )
130 		m_grid[m.from() ]->setPieceType ( m.promotedType() ? m.promotedType() : Queen );
131 
132 	PieceDataMap map = m.removedPieces();
133 	PieceDataMap::const_iterator it = map.constBegin();
134 	PieceDataMap::const_iterator end = map.constEnd();
135 	for ( ; it != end; ++it ) {
136 		delete m_grid.value ( it.key(), nullptr );
137 		m_grid.remove ( it.key() );
138 	}
139 
140 	centerOnPos ( m_grid.value ( m.from() ), m.to() );
141 	m_grid.insert ( m.to(), m_grid.take ( m.from() ) );
142 
143 	map = m.addedPieces();
144 	it = map.constBegin();
145 	end = map.constEnd();
146 	for ( ; it != end; ++it )
147 		addPiece ( it.value().second, it.value().first, it.key() );
148 
149 	if ( m_playerColors & oppositeColor ( m_currentPlayer ) ) {
150 		// We only display motion and danger markers if the next player is a human
151 		if ( Settings::showMotion() ) {
152 			addMarker ( m.from(), Motion );
153 			addMarker ( m.to(), Motion );
154 		}
155 		if ( Settings::showDanger() ) {
156 			bool check = false;
157 			for ( Piece* piece : qAsConst(m_grid) ) {
158 				if ( piece->color() == m_currentPlayer && Manager::self()->rules()->isAttacking ( piece->boardPos() ) ) {
159 					check = true;
160 					addMarker ( piece->boardPos(), Danger );
161 				}
162 			}
163 			if ( check ) {
164 				for ( Piece* piece : qAsConst(m_grid) ) {
165 					if ( piece->color() != m_currentPlayer && piece->pieceType() == King )
166 						addMarker ( piece->boardPos(), Danger );
167 				}
168 			}
169 		}
170 	}
171 
172 	for ( const Move& additionalMove : m.additionalMoves() )
173 		movePiece ( additionalMove );
174 
175 	updateGraphics();
176 }
177 
populate()178 void Board::populate() {
179 	const PieceDataMap pieces = Manager::self()->rules()->startingPieces();
180 	PieceDataMap::const_iterator it = pieces.constBegin();
181 	PieceDataMap::const_iterator end = pieces.constEnd();
182 	for ( ; it != end; ++it )
183 		addPiece ( it.value().second, it.value().first, it.key() );
184 	updateGraphics();
185 }
186 
addTiles()187 void Board::addTiles() {
188 	if ( !m_tiles.isEmpty() ) {
189 		qCWarning(LOG_KNIGHTS) << "Tiles are already present, delete them first";
190 		return;
191 	}
192 	for ( int i = 1; i < 9; ++i ) {
193 		for ( int j = 1; j < 9; ++j ) {
194 			QString key = ( ( i + j ) % 2 == 0 ) ? blackTileKey : whiteTileKey;
195 			Item* tile = new Item ( renderer, key, this, Pos ( i, j ) );
196 			tile->setZValue ( tileZValue );
197 			m_tiles.insert ( Pos ( i, j ), tile );
198 		}
199 	}
200 }
201 
mousePressEvent(QGraphicsSceneMouseEvent * e)202 void Board::mousePressEvent(QGraphicsSceneMouseEvent* e) {
203 	if ( !Manager::self()->canLocalMove() ) {
204 		// It is not the human player's turn
205 		e->ignore();
206 		return;
207 	}
208 
209 	Piece* d_piece = pieceAt ( e->scenePos() );
210 	if ( !d_piece || d_piece->color() != m_currentPlayer ) {
211 		// The piece doesn't belong to the player whose turn it is, or there is no piece
212 		if ( !selectedPiece ) {
213 			e->ignore();
214 			return;
215 		}
216 		Pos from = selectedPiece->boardPos();
217 		Pos to = mapFromScene ( e->scenePos() );
218 		if ( Manager::self()->rules()->legalMoves ( from ).contains ( Move ( from, to ) ) ) {
219 			Move move ( from, to );
220 			move.setFlag ( Move::Take, m_grid.contains ( to ) );
221 
222 			if ( m_grid[from]->pieceType() == Pawn && ( to.second == 1 || to.second == 8 ) ) {
223 				move.setFlag ( Move::Promote, true );
224 				move.setPromotedType ( getPromotedType() );
225 			}
226 			Q_EMIT pieceMoved(move);
227 			selectedPiece = nullptr;
228 		}
229 	} else {
230 		// The active player clicked on his/her own piece
231 		if (d_piece != selectedPiece) {
232 			qDeleteAll ( markers );
233 			markers.clear();
234 
235 			selectedPiece = d_piece;
236 
237 			Pos t_pos = mapFromScene ( e->scenePos() );
238 			QList<Move> t_legalMoves = Manager::self()->rules()->legalMoves ( t_pos );
239 			if ( t_legalMoves.isEmpty() ) {
240 				e->ignore();
241 				return;
242 			}
243 			d_piece->setZValue ( dragZValue );
244 			if ( Settings::showMarker() ) {
245 				for ( const Move& t_move : qAsConst(t_legalMoves) )
246 					addMarker ( t_move.to(), LegalMove );
247 			}
248 			draggedPiece = d_piece;
249 		}
250 		m_draggedPos = e->scenePos();
251 		dragStartPoint = e->screenPos();
252 	}
253 }
254 
mouseReleaseEvent(QGraphicsSceneMouseEvent * e)255 void Board::mouseReleaseEvent(QGraphicsSceneMouseEvent* e) {
256 	Q_UNUSED(e);
257 	draggedPiece = nullptr;
258 }
259 
mouseMoveEvent(QGraphicsSceneMouseEvent * e)260 void Board::mouseMoveEvent(QGraphicsSceneMouseEvent* e) {
261 	if (!(e->buttons() & Qt::LeftButton) || m_dragActive)
262 		return;
263 	if (draggedPiece && ((e->screenPos() - dragStartPoint).manhattanLength() >= QApplication::startDragDistance()) ) {
264 		//initiate a new drag event
265 		drag = new QDrag ( e->widget() );
266 		drag->setMimeData ( new QMimeData() );
267 		m_dragActive = true;
268 		selectedPiece = nullptr;
269 		drag->exec();
270 	}
271 }
272 
dragLeaveEvent(QGraphicsSceneDragDropEvent * e)273 void Board::dragLeaveEvent(QGraphicsSceneDragDropEvent* e) {
274 	Q_UNUSED(e);
275 	if ( !m_dragActive )
276 		return;
277 
278 	qDeleteAll(markers);
279 	markers.clear();
280 	centerOnPos(draggedPiece);
281 	draggedPiece->setZValue(pieceZValue);
282 	draggedPiece = nullptr;
283 	m_dragActive = false;
284 }
285 
dropEvent(QGraphicsSceneDragDropEvent * e)286 void Board::dropEvent(QGraphicsSceneDragDropEvent* e) {
287 	qDeleteAll(markers);
288 	markers.clear();
289 
290 	if (draggedPiece) {
291 		m_dragActive = false;
292 		Pos from = draggedPiece->boardPos();
293 		Pos to = mapFromScene ( e->scenePos() );
294 		Move move ( from, to );
295 		if ( !Manager::self()->rules()->legalMoves ( from ).contains ( move ) )
296 			centerOnPos ( draggedPiece );
297 		else {
298 			if ( m_grid[from]->pieceType() == Pawn && ( to.second == 1 || to.second == 8 ) ) {
299 				move.setFlag ( Move::Promote, true );
300 				move.setPromotedType ( getPromotedType() );
301 			}
302 			Q_EMIT pieceMoved(move);
303 		}
304 		draggedPiece->setZValue(pieceZValue);
305 		draggedPiece = nullptr;
306 	}
307 }
308 
dragEnterEvent(QGraphicsSceneDragDropEvent * e)309 void Board::dragEnterEvent(QGraphicsSceneDragDropEvent* e) {
310 	e->setAccepted(Manager::self()->canLocalMove());
311 }
312 
dragMoveEvent(QGraphicsSceneDragDropEvent * e)313 void Board::dragMoveEvent(QGraphicsSceneDragDropEvent* e) {
314 	if ( !draggedPiece ) {
315 		e->ignore();
316 		return;
317 	}
318 	e->accept();
319 	qreal x = e->scenePos().x() - m_draggedPos.x();
320 	qreal y = e->scenePos().y() - m_draggedPos.y();
321 
322 	draggedPiece->moveBy ( x, y );
323 	m_draggedPos = e->scenePos();
324 }
325 
pieceAt(const QPointF & point)326 Piece* Board::pieceAt(const QPointF& point) {
327 	return m_grid.value(mapFromScene(point), nullptr);
328 }
329 
mapFromScene(const QPointF & point)330 Pos Board::mapFromScene(const QPointF& point) {
331 	Pos pos;
332 	pos.first = ( point.x() - m_boardRect.left() ) / m_tileSize + 1;
333 	pos.second = 1 - ( point.y() - m_boardRect.bottom() ) / m_tileSize;
334 	if ( m_displayedPlayer != White )
335 		pos = Pos ( 9, 9 ) - pos;
336 	return pos;
337 }
338 
mapToScene(Pos pos)339 QPointF Board::mapToScene(Pos pos) {
340 	if ( m_displayedPlayer != White )
341 		pos = Pos ( 9, 9 ) - pos;
342 	QPointF point;
343 	point.setX ( m_boardRect.left() + ( pos.first - 1 ) * m_tileSize );
344 	point.setY ( m_boardRect.bottom() - pos.second * m_tileSize );
345 	return point;
346 }
347 
centerOnPos(Item * item,const Pos & pos,bool animated)348 void Board::centerOnPos(Item* item, const Pos& pos, bool animated) {
349 	item->setBoardPos(pos);
350 	centerOnPos(item, animated);
351 }
352 
centerOnPos(Item * item,bool animated)353 void Board::centerOnPos(Item* item, bool animated) {
354 	item->move(mapToScene(item->boardPos()), m_tileSize, animated);
355 }
356 
centerAndResize(Item * item,QSize size,bool animated)357 void Board::centerAndResize(Item* item, QSize size, bool animated) {
358 	item->moveAndResize(mapToScene ( item->boardPos() ), m_tileSize, size, animated);
359 }
360 
isInBoard(const Pos & pos)361 bool Board::isInBoard(const Pos& pos) {
362 	return pos.first > 0 && pos.first < 9 && pos.second > 0 && pos.second < 9;
363 }
364 
setPlayerColors(Colors colors)365 void Board::setPlayerColors(Colors colors) {
366 	m_playerColors = colors;
367 	if ( m_playerColors & m_currentPlayer )
368 		m_displayedPlayer = m_currentPlayer;
369 	else {
370 		if ( m_playerColors == Black )
371 			m_displayedPlayer = Black;
372 		else
373 			m_displayedPlayer = White;
374 	}
375 	changeDisplayedPlayer();
376 	populate();
377 }
378 
setCurrentColor(Color color)379 void Board::setCurrentColor(Color color) {
380 	m_currentPlayer = color;
381 	Color nextPlayer = m_displayedPlayer;
382 	if ( ( ( m_playerColors & (Black|White) ) == (Black|White) ) && !Settings::flipBoard() )
383 		nextPlayer = White;
384 	else if ( m_playerColors & color )
385 		nextPlayer = color;
386 	if ( m_displayedPlayer != nextPlayer ) {
387 		m_displayedPlayer = nextPlayer;
388 		changeDisplayedPlayer();
389 	}
390 	Q_EMIT activePlayerChanged ( m_currentPlayer );
391 }
392 
393 
addMarker(const Pos & pos,MarkerType type)394 void Board::addMarker(const Pos& pos, MarkerType type) {
395 	QString key;
396 	switch ( type ) {
397 	case LegalMove:
398 		key = legalMarkerKey;
399 		break;
400 	case Danger:
401 		key = dangerMarkerKey;
402 		break;
403 	case Motion:
404 		key = motionMarkerKey;
405 		break;
406 	}
407 	addMarker ( pos, key );
408 }
409 
addMarker(const Pos & pos,const QString & spriteKey)410 void Board::addMarker(const Pos& pos, const QString& spriteKey) {
411 	if ( markers.contains ( pos ) ) {
412 		// Prevent two markers (usually Motion and Danger) from being on the same square
413 		delete markers[pos];
414 	}
415 	Item* marker = new Item ( renderer, spriteKey, this, pos );
416 	centerOnPos ( marker, false );
417 	marker->setRenderSize ( QSizeF ( m_tileSize, m_tileSize ).toSize() );
418 	marker->setZValue ( legalMarkerZValue );
419 	markers.insert ( pos, marker );
420 }
421 
updateTheme()422 void Board::updateTheme() {
423 	delete m_background;
424 	qDebug()<<"background key " << backgroundKey;
425 	if ( renderer->spriteExists ( backgroundKey ) ) {
426 		m_background = new Item ( renderer, backgroundKey, this, Pos() );
427 		m_background->setZValue ( backgroundZValue );
428 	} else
429 		m_background = nullptr;
430 
431 	qDeleteAll ( m_borders );
432 	m_borders.clear();
433 	qDeleteAll ( m_notations );
434 	m_notations.clear();
435 	m_displayBorders = Settings::borderDisplayType() != Settings::EnumBorderDisplayType::None
436 	                   && renderer->spriteExists ( lrBorderKey )
437 	                   && renderer->spriteExists ( tbBorderKey );
438 	m_displayNotations = Settings::borderDisplayType() == Settings::EnumBorderDisplayType::Notation
439 	                     && renderer->spriteExists ( whiteLettersKey )
440 	                     && renderer->spriteExists ( blackLettersKey )
441 	                     && renderer->spriteExists ( whiteNumbersKey )
442 	                     && renderer->spriteExists ( blackNumbersKey );
443 	if ( m_displayBorders ) {
444 		m_borders << new Item ( renderer, tbBorderKey, this, Pos() );
445 
446 		Item *tItem = new Item ( renderer, lrBorderKey, this, Pos() );
447 		tItem->setRotation ( 180 );
448 		m_borders << tItem;
449 
450 		tItem = new Item ( renderer, tbBorderKey, this, Pos() );
451 		tItem->setRotation ( 180 );
452 		m_borders << tItem;
453 
454 		m_borders << new Item ( renderer, lrBorderKey, this, Pos() );
455 		for ( Item* item : qAsConst(m_borders) )
456 			item->setZValue ( borderZValue );
457 	}
458 	if ( m_displayNotations ) {
459 		QString lettersKey;
460 		QString numbersKey;
461 		if ( m_displayedPlayer == White ) {
462 			lettersKey = whiteLettersKey;
463 			numbersKey = whiteNumbersKey;
464 		} else {
465 			lettersKey = blackLettersKey;
466 			numbersKey = blackNumbersKey;
467 		}
468 		m_notations << new Item ( renderer, lettersKey, this, Pos() );
469 		m_notations << new Item ( renderer, numbersKey, this, Pos() );
470 		m_notations << new Item ( renderer, lettersKey, this, Pos() );
471 		m_notations << new Item ( renderer, numbersKey, this, Pos() );
472 		for ( Item* item : qAsConst(m_notations) )
473 			item->setZValue ( notationZValue );
474 	}
475 	addTiles();
476 	updateGraphics();
477 }
478 
updateGraphics()479 void Board::updateGraphics() {
480 	if ( m_background )
481 		m_background->setRenderSize ( sceneRect().size().toSize() );
482 	QSizeF tileSize = renderer->boundsOnSprite ( whiteTileKey ).size();
483 	QSizeF boardSize = 8 * tileSize;
484 	qreal sideMargin;
485 	qreal topMargin;
486 	if ( m_displayBorders ) {
487 		sideMargin = renderer->boundsOnSprite ( lrBorderKey ).width();
488 		topMargin = renderer->boundsOnSprite ( tbBorderKey ).height();
489 	} else {
490 		sideMargin = 0.0;
491 		topMargin = 0.0;
492 	}
493 	boardSize = boardSize + 2 * QSize ( sideMargin, topMargin );
494 	qreal ratio = qMin ( sceneRect().width() / boardSize.width(), sceneRect().height() / boardSize.height() );
495 	sideMargin *= ratio;
496 	topMargin *= ratio;
497 
498 	QSizeF tpSize = tileSize * ratio;
499 	m_tileSize = qFloor ( qMin ( tpSize.width(), tpSize.height() ) );
500 	/*
501 	if ( m_displayBorders )
502 	{
503 	    if (  m_tileSize % 2 )
504 	    {
505 	        m_tileSize -= 1;
506 	    }
507 	    sideMargin = m_tileSize / 2;
508 	    topMargin = m_tileSize / 2;
509 	}
510 	*/
511 
512 	QSize hBorderSize = QSize ( 8 * m_tileSize + 2 * qRound ( sideMargin ), qRound ( topMargin ) );
513 	QSize vBorderSize = QSize ( qRound ( sideMargin ), 8 * m_tileSize );
514 	int hBorderMargin = qRound ( topMargin );
515 	int vBorderMargin = qRound ( sideMargin );
516 
517 
518 	sideMargin = qMax ( sideMargin, ( sceneRect().width() - 8 * m_tileSize ) / 2 );
519 	topMargin = qMax ( topMargin, ( sceneRect().height() - 8 * m_tileSize ) / 2 );
520 	m_boardRect = QRect( sceneRect().topLeft().toPoint() + QPoint( sideMargin, topMargin ),
521 	                     QSize( m_tileSize, m_tileSize ) * 8);
522 
523 	QSize tSize = QSizeF ( m_tileSize, m_tileSize ).toSize();
524 	/*
525 	 * For historical reasons, QRect's
526 	 */
527 	QPointF topBorderPoint = m_boardRect.topRight() + QPoint ( vBorderMargin, 0 );
528 	QPointF rightBorderPoint = m_boardRect.bottomRight() + QPoint ( vBorderMargin, 0 );
529 	QPointF bottomBorderPoint = m_boardRect.bottomLeft() - QPoint ( vBorderMargin, 0 );
530 	QPointF leftBorderPoint = m_boardRect.topLeft() - QPoint ( vBorderMargin, 0 );
531 
532 	for ( Piece* p : qAsConst(m_grid) )
533 		centerAndResize ( p, tSize );
534 	for ( Item* t : qAsConst(m_tiles) )
535 		centerAndResize ( t, tSize, Settings::animateBoard() );
536 	for ( Item* t : qAsConst(markers) )
537 		centerAndResize ( t, tSize );
538 	if ( m_displayBorders ) {
539 		m_borders[0]->moveAndResize ( bottomBorderPoint, m_tileSize, hBorderSize, Settings::animateBoard() );
540 		m_borders[1]->moveAndResize ( rightBorderPoint, m_tileSize, vBorderSize, Settings::animateBoard() );
541 		m_borders[2]->moveAndResize ( topBorderPoint, m_tileSize, hBorderSize, Settings::animateBoard() );
542 		m_borders[3]->moveAndResize ( leftBorderPoint, m_tileSize, vBorderSize, Settings::animateBoard() );
543 	}
544 	if ( m_displayNotations ) {
545 		m_notations[0]->moveAndResize ( bottomBorderPoint, m_tileSize, hBorderSize, Settings::animateBoard() );
546 		m_notations[1]->moveAndResize ( m_boardRect.topRight(), m_tileSize, vBorderSize, Settings::animateBoard() );
547 		m_notations[2]->moveAndResize ( m_boardRect.topLeft() - QPointF ( vBorderMargin, hBorderMargin ), m_tileSize, hBorderSize, Settings::animateBoard() );
548 		m_notations[3]->moveAndResize ( leftBorderPoint, m_tileSize, vBorderSize, Settings::animateBoard() );
549 	}
550 }
551 
changeDisplayedPlayer()552 void Board::changeDisplayedPlayer() {
553 	for ( Piece* p : qAsConst(m_grid) )
554 		centerOnPos ( p );
555 	for ( Item* i : qAsConst(markers) )
556 		centerOnPos ( i );
557 	if ( Settings::animateBoard() ) {
558 		for ( Item* i : qAsConst(m_tiles) )
559 			centerOnPos ( i );
560 	}
561 	if ( m_displayNotations ) {
562 		if ( m_displayedPlayer == White ) {
563 			m_notations[0]->setSpriteKey ( whiteLettersKey );
564 			m_notations[1]->setSpriteKey ( whiteNumbersKey );
565 			m_notations[2]->setSpriteKey ( whiteLettersKey );
566 			m_notations[3]->setSpriteKey ( whiteNumbersKey );
567 		} else {
568 			m_notations[0]->setSpriteKey ( blackLettersKey );
569 			m_notations[1]->setSpriteKey ( blackNumbersKey );
570 			m_notations[2]->setSpriteKey ( blackLettersKey );
571 			m_notations[3]->setSpriteKey ( blackNumbersKey );
572 		}
573 	}
574 	Q_EMIT displayedPlayerChanged(m_displayedPlayer);
575 }
576 
getPromotedType()577 PieceType Board::getPromotedType() {
578 	PieceType piece = Queen;
579 	QPointer<QDialog> dialog = new QDialog();
580 	dialog->setWindowTitle(i18nc("@title:window", "Promotion"));
581 	QWidget *widget = new QWidget();
582 	QVBoxLayout *layout = new QVBoxLayout();
583 	QDialogButtonBox *bBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
584 	bBox->button(QDialogButtonBox::Ok)->setText(i18n("Promote"));
585 	bBox->button(QDialogButtonBox::Ok)->setDefault(true);
586 
587 	Ui::PromotionWidget ui;
588 	ui.setupUi ( widget );
589 
590 	layout->addWidget ( widget );
591 	layout->addWidget ( bBox );
592 	dialog->setLayout ( layout );
593 
594 	connect (bBox, &QDialogButtonBox::accepted, dialog, &QDialog::accept);
595 	connect (bBox, &QDialogButtonBox::rejected, dialog, &QDialog::reject);
596 
597 	if ( dialog->exec() == QDialog::Accepted ) {
598 		if ( ui.radioButtonKnight->isChecked() )
599 			piece = Knight;
600 		else if ( ui.radioButtonBishop->isChecked() )
601 			piece = Bishop;
602 		else if ( ui.radioButtonRook->isChecked() )
603 			piece = Rook;
604 	}
605 	delete dialog;
606 	return piece;
607 }
608