2 Copyright (c) 2006 Paolo Capriotti <p.capriotti@sns.it>
3 (c) 2006 Maurizio Monge <maurizio.monge@kdemail.net>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
13 #include <QApplication>
14 #include <QMouseEvent>
18 #include "piecesprite.h"
19 #include "animation.h"
20 #include "boardsprite.h"
21 #include "pointconverter.h"
22 #include "entities/userentity.h"
23 #include "mainanimation.h"
26 using namespace boost
;
28 /** inherit instead of typedef to ease forward declaration :) */
29 class BoardTags
: public std::map
<QString
, std::map
<Point
, boost::shared_ptr
<Canvas::Pixmap
> > > {
32 Board::Board(Canvas::Abstract
* parent
)
35 , m_hinting_pos(Point::invalid())
36 , selection(Point::invalid())
37 , lastSelection(Point::invalid()) {
39 m_tags
= BoardTagsPtr(new BoardTags
);
41 m_canvas_background
= new Canvas::Group(this);
42 m_canvas_background
->lower();
43 m_canvas_background
->show();
45 m_pieces_group
= new Canvas::Group(this);
46 m_pieces_group
->show();
52 delete m_pieces_group
;
54 while(!m_canvas_background
->items()->isEmpty())
55 delete m_canvas_background
->items()->first();
56 delete m_canvas_background
;
59 void Board::mySettingsChanged() {
60 Settings s_border
= settings
.group("board-border");
61 m_show_border
= s_border
.flag("visible", true);
62 m_border_color
= (s_border
["color"] |= QColor(Qt::white
));
63 m_border_text_color
= (s_border
["text-color"] |= QColor(Qt::black
));
64 m_border_font
= (s_border
["font"] |= QApplication::font());
69 void Board::settingsChanged() {
70 PieceGroup::settingsChanged();
74 void Board::updateBackground() {
75 while(!m_canvas_background
->items()->isEmpty())
76 delete m_canvas_background
->items()->first();
78 Loader::PixmapOrMap bg
= m_tags_loader
.getPixmapMap("background");
79 if(const QPixmap
* p
= boost::get
<QPixmap
>(&bg
)) {
80 Canvas::TiledPixmap
*t
= new Canvas::TiledPixmap(*p
, boardRect().size(), QPoint(),
81 true, m_canvas_background
);
84 else if(const Loader::PixmapMap
* p
= boost::get
<Loader::PixmapMap
>(&bg
)) {
85 for(Loader::PixmapMap::const_iterator it
= p
->begin(); it
!= p
->end(); ++it
) {
86 Canvas::TiledPixmap
*t
= new Canvas::TiledPixmap(it
->second
, it
->first
.size(),
87 QPoint(), true, m_canvas_background
);
88 t
->moveTo(it
->first
.topLeft());
94 boost::shared_ptr
<Canvas::Pixmap
> Board::addTag(const QString
& name
, Point pt
, bool over
) {
95 if(!m_sprites
.valid(pt
))
96 return boost::shared_ptr
<Canvas::Pixmap
>();
98 QPixmap p
= m_tags_loader(name
);
99 boost::shared_ptr
<Canvas::Pixmap
> item
=
100 boost::shared_ptr
<Canvas::Pixmap
>(new Canvas::Pixmap(p
, this));
101 item
->moveTo(converter()->toReal(pt
));
103 item
->stackOver(m_pieces_group
);
105 item
->stackUnder(m_pieces_group
);
108 (*m_tags
)[name
][pt
] = item
;
112 void Board::clearTags(const QString
& name
) {
116 void Board::clearTags() {
120 void Board::setTags(const QString
& name
, Point p1
, Point p2
, Point p3
,
121 Point p4
, Point p5
, Point p6
) {
122 //TODO: maybe this could be optimized a bit
132 void Board::recreateBorder() {
133 m_border_margins
.clear();
134 m_border_items
.clear();
136 if(!m_show_border
|| m_border_coords
.size() == 0) {
141 QFontMetrics
fm(m_border_font
);
142 m_border_size
= std::max( fm
.height(), fm
.boundingRect("H").width() );
143 m_border_asc
= fm
.ascent() + (m_border_size
-fm
.height())/2;
145 for(int w
= 0; w
<2; w
++)
146 for(int i
=0;i
<4;i
++) {
147 Canvas::Rectangle
*item
= new Canvas::Rectangle(
148 w
? m_border_text_color
: m_border_color
, QSize(), this);
150 m_border_margins
.push_back( boost::shared_ptr
<Canvas::Rectangle
>( item
));
153 Point s
= m_sprites
.getSize();
154 for(int w
= 0; w
<2; w
++)
155 for(int i
= 0;i
<s
.x
;i
++) {
156 int c
= w
? i
: i
+s
.x
+s
.y
;
157 QString l
= m_border_coords
.size()>c
? m_border_coords
[c
] : QString();
158 Canvas::Item
*item
= new Canvas::Text( l
, m_border_text_color
, m_border_font
,
159 Canvas::Text::HCenter
, Canvas::Text::VBaseline
, this);
161 m_border_items
.push_back( boost::shared_ptr
<Canvas::Item
>( item
));
164 for(int w
= 0; w
<2; w
++)
165 for(int i
= 0;i
<s
.y
;i
++) {
166 int c
= w
? i
+s
.x
: i
+2*s
.x
+s
.y
;
167 QString n
= m_border_coords
.size()>c
? m_border_coords
[c
] : QString();
168 Canvas::Item
*item
= new Canvas::Text( n
, m_border_text_color
, m_border_font
,
169 Canvas::Text::HCenter
, Canvas::Text::VBaseline
, this);
171 m_border_items
.push_back( boost::shared_ptr
<Canvas::Item
>( item
));
174 m_pieces_group
->raise();
179 void Board::updateBorder() {
184 for(int w
= 0; w
<2; w
++)
185 for(int i
= 0;i
<m_sprites
.getSize().x
;i
++) {
186 int x
= (m_flipped
? (m_sprites
.getSize().x
-1-i
):i
)*m_square_size
+m_square_size
/2;
187 int y
= m_border_asc
+(w
?0:m_border_size
+m_square_size
*m_sprites
.getSize().y
)-m_border_size
;
188 m_border_items
[at
++]->moveTo(x
, y
);
191 for(int w
= 0; w
<2; w
++)
192 for(int i
= 0;i
<m_sprites
.getSize().y
;i
++) {
193 int x
= -m_border_size
/2+(w
?0:m_border_size
+m_square_size
*m_sprites
.getSize().x
);
194 int y
= (!m_flipped
? (m_sprites
.getSize().y
-1-i
):i
)*m_square_size
195 +m_square_size
/2-m_border_size
/2+m_border_asc
;
196 m_border_items
[at
++]->moveTo(x
, y
);
199 m_border_margins
[0]->moveTo(-m_border_size
,-m_border_size
);
200 m_border_margins
[0]->setSize(QSize(2*m_border_size
+m_square_size
*m_sprites
.getSize().x
,
202 m_border_margins
[4]->moveTo(-m_border_size
-1,-m_border_size
-1);
203 m_border_margins
[4]->setSize(QSize(2*m_border_size
+m_square_size
*m_sprites
.getSize().x
+2, 1));
205 m_border_margins
[1]->moveTo(-m_border_size
,m_square_size
*m_sprites
.getSize().y
);
206 m_border_margins
[1]->setSize(QSize(2*m_border_size
+m_square_size
*m_sprites
.getSize().x
,
208 m_border_margins
[5]->moveTo(-m_border_size
-1,m_square_size
*m_sprites
.getSize().y
+m_border_size
);
209 m_border_margins
[5]->setSize(QSize(2*m_border_size
+m_square_size
*m_sprites
.getSize().x
+2, 1));
211 m_border_margins
[2]->moveTo(-m_border_size
,0);
212 m_border_margins
[2]->setSize(QSize(m_border_size
, m_square_size
*m_sprites
.getSize().y
));
213 m_border_margins
[6]->moveTo(-m_border_size
-1,-m_border_size
);
214 m_border_margins
[6]->setSize(QSize(1, m_square_size
*m_sprites
.getSize().y
+2*m_border_size
));
216 m_border_margins
[3]->moveTo(m_square_size
*m_sprites
.getSize().x
, 0);
217 m_border_margins
[3]->setSize(QSize(m_border_size
, m_square_size
*m_sprites
.getSize().y
));
218 m_border_margins
[7]->moveTo(m_square_size
*m_sprites
.getSize().x
+m_border_size
, -m_border_size
);
219 m_border_margins
[7]->setSize(QSize(1, m_square_size
*m_sprites
.getSize().y
+2*m_border_size
));
222 void Board::createGrid(Point p
, const QStringList
& border_coords
) {
223 m_border_coords
= border_coords
;
224 m_sprites
= PieceGrid(p
.x
,p
.y
);
228 QRect
Board::computeRect(Point p
) const {
229 QPoint realPoint
= converter()->toReal(p
);
230 return squareRect(realPoint
.x(), realPoint
.y());
233 QRect
Board::squareRect(int x
, int y
) const {
234 return QRect(x
, y
, m_square_size
, m_square_size
);
237 QRegion
Board::computeRegion(Point p
) const {
238 return QRegion(computeRect(p
));
242 void Board::setSelection(const Point
& p
) {
243 lastSelection
= selection
;
245 setTags("selection", p
);
248 void Board::cancelSelection() {
249 lastSelection
= selection
;
250 selection
= Point::invalid();
251 clearTags("selection");
256 void Board::setPremove(const NormalUserMove
& premove
) {
257 m_premove_from
= premove
.from
;
258 m_premove_to
= premove
.to
;
259 setTags("premove", m_premove_from
, m_premove_to
);
262 void Board::setPremove(const DropUserMove
& premove
) {
263 m_premove_from
= Point::invalid();
264 m_premove_to
= premove
.m_to
;
265 setTags("premove", m_premove_to
);
268 void Board::setPremove(const Premove
& premove
) {
269 setPremove(premove
.toUserMove());
272 void Board::cancelPremove() {
273 m_premove_from
= Point::invalid();
274 m_premove_to
= Point::invalid();
275 clearTags("premove");
278 void Board::updateSprites() {
279 // adjust piece positions
280 for (Point i
= m_sprites
.first(); i
<= m_sprites
.last(); i
= m_sprites
.next(i
)) {
281 boost::shared_ptr
<PieceSprite
> p
= m_sprites
[i
].sprite();
285 p
->setPixmap( m_loader( m_sprites
[i
].name() ) );
291 void Board::updateTags() {
292 for(BoardTags::iterator tit
= m_tags
->begin(); tit
!= m_tags
->end(); ++tit
)
293 for(std::map
<Point
, boost::shared_ptr
<Canvas::Pixmap
> >::iterator pt
=
294 tit
->second
.begin(); pt
!= tit
->second
.end(); ++pt
) {
295 pt
->second
->moveTo(converter()->toReal(pt
->first
));
296 pt
->second
->setPixmap(m_tags_loader(tit
->first
));
301 bool Board::doMove(const NormalUserMove
& m
) {
302 if (m_entity
.lock()->oneClickMoves() || m_entity
.lock()->validTurn(m
.from
) == Entity::Moving
) {
303 AbstractMove::Ptr mv
= m_entity
.lock()->testMove(m
);
305 m_entity
.lock()->executeMove(mv
);
310 std::cout
<< "invalid move" << std::endl
;
311 emit
error(InvalidMove
);
317 void Board::onResize(int new_size
, bool force_reload
) {
318 if(m_square_size
== new_size
&& !force_reload
)
321 PieceGroup::onResize(new_size
);
323 // update the size of the tag loader
324 m_tags_loader
.setSize(m_square_size
);
326 // update canvas background
329 // update the sprites
339 void Board::onMousePress(const QPoint
& pos
, int button
) {
340 Point point
= converter()->toLogical(pos
);
341 if (!m_sprites
.valid(point
))
346 if (button
== Qt::LeftButton
) {
347 if (m_entity
.lock()->oneClickMoves()) {
348 NormalUserMove
m(Point::invalid(), point
);
349 m_entity
.lock()->setPromotion(m
);
353 shared_ptr
<PieceSprite
> piece
= m_sprites
[point
].sprite();
355 if (piece
&& m_entity
.lock()->movable(point
)) {
357 m_drag_info
= DragInfoPtr(new DragInfo(point
, pos
, piece
,
358 m_entity
.lock()->validTurn(point
)) );
362 // if selection is valid, (pre)move to point
363 else if (selection
!= Point::invalid()) {
364 piece
= m_sprites
[selection
].sprite();
366 NormalUserMove
m(selection
, point
);
367 m_entity
.lock()->setPromotion(m
);
369 switch(m_entity
.lock()->validTurn(selection
)) {
371 case UserEntity::Moving
:
376 case UserEntity::Premoving
:
377 if (m_entity
.lock()->testPremove(m
)) {
378 m_entity
.lock()->addPremove(m
);
395 else if (button
== Qt::RightButton
) {
397 if (point
== m_premove_from
|| point
== m_premove_to
)
399 m_entity
.lock()->handleRightClick(point
);
405 void Board::onMouseRelease(const QPoint
& pos
, int button
) {
406 Point point
= converter()->toLogical(pos
);
410 if (button
== Qt::LeftButton
) {
413 // Q_ASSERT(m_drag_info->piece);
414 Q_ASSERT(m_drag_info
->sprite
);
417 // remove valid move highlighting
418 clearTags("validmove");
420 // toggle selection if the piece didn't move
421 if (m_drag_info
->from
== point
) {
422 if (lastSelection
== point
)
429 NormalUserMove
m(m_drag_info
->from
, point
, true);
430 if (!m_sprites
.valid(point
))
431 m
.to
= Point::invalid();
433 m_entity
.lock()->setPromotion(m
);
435 switch(m_entity
.lock()->validTurn(m_drag_info
->from
)) {
437 case UserEntity::Moving
:
442 case UserEntity::Premoving
:
443 if (m_entity
.lock()->testPremove(m
)) {
444 m_entity
.lock()->addPremove(m
);
454 shared_ptr
<PieceSprite
> s
= m_sprites
[m_drag_info
->from
].sprite();
455 if (!moved
&& s
&& s
->pos() != converter()->toReal(m_drag_info
->from
)) {
457 QPoint real
= converter()->toReal(m_drag_info
->from
);
458 if( (point
== m_drag_info
->from
) ? !m_anim_movement
: !m_anim_fade
)
459 enqueue(shared_ptr
<Animation
>(new InstantAnimation(s
, real
)));
460 else if (point
== m_drag_info
->from
)
461 enqueue(shared_ptr
<Animation
>(new MovementAnimation(s
, real
)));
463 enqueue(shared_ptr
<Animation
>(new TeleportAnimation(s
, s
->pos(), real
)));
466 m_drag_info
= DragInfoPtr();
473 void Board::onMouseMove(const QPoint
& pos
, int /*button*/) {
474 Point point
= converter()->toLogical(pos
);
477 Q_ASSERT(m_drag_info
->sprite
);
478 // check drag threshold
479 if (!m_drag_info
->dragStarted
) {
480 QPoint delta
= pos
- m_drag_info
->real
;
481 if (delta
.x() * delta
.x() + delta
.y() * delta
.y() > DragInfo::DRAG_THRESHOLD
) {
482 m_drag_info
->dragStarted
= true;
485 if (m_drag_info
->dragStarted
)
486 m_drag_info
->sprite
->moveTo(pos
- QPoint(m_square_size
/ 2, m_square_size
/ 2) );
488 // highlight valid moves
489 NormalUserMove
move(m_drag_info
->from
, point
);
490 bool valid
= m_sprites
.valid(point
);
492 UserEntity::Action action
= m_entity
.lock()->validTurn(m_drag_info
->from
);
493 if (action
== UserEntity::Moving
)
494 valid
= m_entity
.lock()->testMove(move
);
498 setTags("validmove", point
);
500 clearTags("validmove");
502 else if (m_entity
.lock()->oneClickMoves()) {
503 if(point
== m_hinting_pos
)
506 AbstractPiece::Ptr hint
;
508 if (m_sprites
.valid(point
)) {
509 if (AbstractMove::Ptr move
= m_entity
.lock()->testMove(
510 NormalUserMove(Point::invalid(), point
))) {
512 hint
= m_entity
.lock()->moveHint(move
);
516 updateHinting(point
, hint
);
520 void Board::onPositionChanged() {
521 if (m_entity
.lock() && m_entity
.lock()->oneClickMoves() && m_sprites
.valid(m_hinting_pos
)) {
522 AbstractPiece::Ptr hint
;
524 if (AbstractMove::Ptr move
= m_entity
.lock()->testMove(
525 NormalUserMove(Point::invalid(), m_hinting_pos
)) ) {
527 hint
= m_entity
.lock()->moveHint(move
);
530 updateHinting(m_hinting_pos
, hint
);
534 void Board::onMouseLeave() {
535 updateHinting(Point::invalid(), AbstractPiece::Ptr());
538 void Board::updateHinting(Point pt
, AbstractPiece::Ptr piece
) {
539 if(!m_sprites
.valid(pt
))
540 piece
= AbstractPiece::Ptr();
542 if(!piece
|| !m_sprites
.valid(pt
)) {
543 if(m_hinting
.sprite()) {
545 enqueue( boost::shared_ptr
<Animation
>(new FadeAnimation(m_hinting
.sprite(),
546 m_hinting
.sprite()->pos(), 160, 0)) );
548 enqueue( boost::shared_ptr
<Animation
>(new CaptureAnimation(m_hinting
.sprite())) );
551 m_hinting_pos
= Point::invalid();
552 m_hinting
= Element();
555 if(pt
== m_hinting_pos
) {
556 if(!piece
->equals(m_hinting
.piece())) {
557 m_hinting
= Element(piece
, m_hinting
.sprite());
558 m_hinting
.sprite()->setPixmap(m_loader(piece
->name()));
562 if(m_hinting
.sprite()) {
564 enqueue( boost::shared_ptr
<Animation
>(new FadeAnimation(m_hinting
.sprite(),
565 m_hinting
.sprite()->pos(), 160, 0)) );
567 enqueue( boost::shared_ptr
<Animation
>(new CaptureAnimation(m_hinting
.sprite())) );
570 QPixmap pix
= m_loader(piece
->name());
571 boost::shared_ptr
<PieceSprite
> sprite
= createSprite(pix
, pt
);
572 sprite
->setOpacity(160);
577 m_hinting
= Element(piece
, sprite
);
580 enqueue( boost::shared_ptr<Animation>(new FadeAnimation(m_hinting.sprite(),
581 m_hinting.sprite()->pos(), 0, 160)) );
583 m_hinting.sprite()->setOpacity(160);
584 enqueue(boost::shared_ptr<Animation>(new DropAnimation(m_hinting.sprite())) );
590 void Board::reset() {
594 m_main_animation
->stop();
597 void Board::flip(bool flipped
)
599 if (m_flipped
!= flipped
) {
602 // update sprite positions
603 for (Point i
= m_sprites
.first(); i
<= m_sprites
.last(); i
= m_sprites
.next(i
))
604 if (m_sprites
[i
].sprite())
605 animatePiece(m_sprites
[i
].sprite(), i
, 1.0);
612 void Board::draggingOn(AbstractPiece::Ptr piece
, const QPoint
& point
) {
613 Point to
= converter()->toLogical(point
);
615 if (m_sprites
.valid(to
))
616 switch(m_entity
.lock()->validTurn(piece
->color())) {
617 case UserEntity::Moving
: {
618 DropUserMove
m(piece
, to
);
619 AbstractMove::Ptr mv
= m_entity
.lock()->testMove(m
);
621 setTags("validmove", to
);
627 case UserEntity::Premoving
:
628 setTags("validmove", to
);
635 clearTags("validmove");
638 bool Board::dropOn(AbstractPiece::Ptr piece
, const QPoint
& point
) {
640 Point to
= converter()->toLogical(point
);
641 if (!m_sprites
.valid(to
))
644 clearTags("validmove");
646 switch(m_entity
.lock()->validTurn(piece
->color())) {
648 case UserEntity::Moving
: {
649 DropUserMove
m(piece
, to
);
650 AbstractMove::Ptr mv
= m_entity
.lock()->testMove(m
);
652 m_entity
.lock()->executeMove(mv
);
658 case UserEntity::Premoving
: {
659 DropUserMove
m(piece
, to
);
660 if (m_entity
.lock()->testPremove(m
)) {
661 m_entity
.lock()->addPremove(m
);
670 std::cout
<< "invalid move" << std::endl
;
671 emit
error(InvalidMove
);