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>
19 #include "animation.h"
20 #include "pointconverter.h"
21 #include "entities/userentity.h"
22 #include "mainanimation.h"
25 using namespace boost
;
27 /** inherit instead of typedef to ease forward declaration :) */
28 class BoardTags
: public std::map
<QString
, std::map
<Point
, boost::shared_ptr
<Canvas::Pixmap
> > > {
31 Board::Board(Canvas::Abstract
* parent
)
34 , m_hinting_pos(Point::invalid())
35 , selection(Point::invalid())
36 , lastSelection(Point::invalid()) {
38 m_tags
= BoardTagsPtr(new BoardTags
);
40 m_canvas_background
= new Canvas::Group(this);
41 m_canvas_background
->lower();
42 m_canvas_background
->show();
44 m_pieces_group
= new Canvas::Group(this);
45 m_pieces_group
->show();
51 delete m_pieces_group
;
53 while(!m_canvas_background
->items()->isEmpty())
54 delete m_canvas_background
->items()->first();
55 delete m_canvas_background
;
58 void Board::mySettingsChanged() {
59 Settings s_border
= settings
.group("board-border");
60 m_show_border
= s_border
.flag("visible", true);
61 m_border_color
= (s_border
["color"] |= QColor(Qt::white
));
62 m_border_text_color
= (s_border
["text-color"] |= QColor(Qt::black
));
63 m_border_font
= (s_border
["font"] |= QApplication::font());
68 void Board::settingsChanged() {
69 PieceGroup::settingsChanged();
73 void Board::updateBackground() {
74 while(!m_canvas_background
->items()->isEmpty())
75 delete m_canvas_background
->items()->first();
77 Loader::PixmapOrMap bg
= m_tags_loader
.getPixmapMap("background");
78 if(const QPixmap
* p
= boost::get
<QPixmap
>(&bg
)) {
79 Canvas::TiledPixmap
*t
= new Canvas::TiledPixmap(*p
, boardRect().size(), QPoint(),
80 true, m_canvas_background
);
83 else if(const Loader::PixmapMap
* p
= boost::get
<Loader::PixmapMap
>(&bg
)) {
84 for(Loader::PixmapMap::const_iterator it
= p
->begin(); it
!= p
->end(); ++it
) {
85 Canvas::TiledPixmap
*t
= new Canvas::TiledPixmap(it
->second
, it
->first
.size(),
86 QPoint(), true, m_canvas_background
);
87 t
->moveTo(it
->first
.topLeft());
93 boost::shared_ptr
<Canvas::Pixmap
> Board::addTag(const QString
& name
, Point pt
, bool over
) {
94 if(!m_sprites
.valid(pt
))
95 return boost::shared_ptr
<Canvas::Pixmap
>();
97 QPixmap p
= m_tags_loader(name
);
98 boost::shared_ptr
<Canvas::Pixmap
> item
=
99 boost::shared_ptr
<Canvas::Pixmap
>(new Canvas::Pixmap(p
, this));
100 item
->moveTo(converter()->toReal(pt
));
102 item
->stackOver(m_pieces_group
);
104 item
->stackUnder(m_pieces_group
);
107 (*m_tags
)[name
][pt
] = item
;
111 void Board::clearTags(const QString
& name
) {
115 void Board::clearTags() {
119 void Board::setTags(const QString
& name
, Point p1
, Point p2
, Point p3
,
120 Point p4
, Point p5
, Point p6
) {
121 //TODO: maybe this could be optimized a bit
131 void Board::recreateBorder() {
132 m_border_margins
.clear();
133 m_border_items
.clear();
135 if(!m_show_border
|| m_border_coords
.size() == 0) {
140 QFontMetrics
fm(m_border_font
);
141 m_border_size
= std::max( fm
.height(), fm
.boundingRect("H").width() );
142 m_border_asc
= fm
.ascent() + (m_border_size
-fm
.height())/2;
144 for(int w
= 0; w
<2; w
++)
145 for(int i
=0;i
<4;i
++) {
146 Canvas::Rectangle
*item
= new Canvas::Rectangle(
147 w
? m_border_text_color
: m_border_color
, QSize(), this);
149 m_border_margins
.push_back( boost::shared_ptr
<Canvas::Rectangle
>( item
));
152 Point s
= m_sprites
.getSize();
153 for(int w
= 0; w
<2; w
++)
154 for(int i
= 0;i
<s
.x
;i
++) {
155 int c
= w
? i
: i
+s
.x
+s
.y
;
156 QString l
= m_border_coords
.size()>c
? m_border_coords
[c
] : QString();
157 Canvas::Item
*item
= new Canvas::Text( l
, m_border_text_color
, m_border_font
,
158 Canvas::Text::HCenter
, Canvas::Text::VBaseline
, this);
160 m_border_items
.push_back( boost::shared_ptr
<Canvas::Item
>( item
));
163 for(int w
= 0; w
<2; w
++)
164 for(int i
= 0;i
<s
.y
;i
++) {
165 int c
= w
? i
+s
.x
: i
+2*s
.x
+s
.y
;
166 QString n
= m_border_coords
.size()>c
? m_border_coords
[c
] : QString();
167 Canvas::Item
*item
= new Canvas::Text( n
, m_border_text_color
, m_border_font
,
168 Canvas::Text::HCenter
, Canvas::Text::VBaseline
, this);
170 m_border_items
.push_back( boost::shared_ptr
<Canvas::Item
>( item
));
173 m_pieces_group
->raise();
178 void Board::updateBorder() {
183 for(int w
= 0; w
<2; w
++)
184 for(int i
= 0;i
<m_sprites
.getSize().x
;i
++) {
185 int x
= (m_flipped
? (m_sprites
.getSize().x
-1-i
):i
)*m_square_size
+m_square_size
/2;
186 int y
= m_border_asc
+(w
?0:m_border_size
+m_square_size
*m_sprites
.getSize().y
)-m_border_size
;
187 m_border_items
[at
++]->moveTo(x
, y
);
190 for(int w
= 0; w
<2; w
++)
191 for(int i
= 0;i
<m_sprites
.getSize().y
;i
++) {
192 int x
= -m_border_size
/2+(w
?0:m_border_size
+m_square_size
*m_sprites
.getSize().x
);
193 int y
= (!m_flipped
? (m_sprites
.getSize().y
-1-i
):i
)*m_square_size
194 +m_square_size
/2-m_border_size
/2+m_border_asc
;
195 m_border_items
[at
++]->moveTo(x
, y
);
198 m_border_margins
[0]->moveTo(-m_border_size
,-m_border_size
);
199 m_border_margins
[0]->setSize(QSize(2*m_border_size
+m_square_size
*m_sprites
.getSize().x
,
201 m_border_margins
[4]->moveTo(-m_border_size
-1,-m_border_size
-1);
202 m_border_margins
[4]->setSize(QSize(2*m_border_size
+m_square_size
*m_sprites
.getSize().x
+2, 1));
204 m_border_margins
[1]->moveTo(-m_border_size
,m_square_size
*m_sprites
.getSize().y
);
205 m_border_margins
[1]->setSize(QSize(2*m_border_size
+m_square_size
*m_sprites
.getSize().x
,
207 m_border_margins
[5]->moveTo(-m_border_size
-1,m_square_size
*m_sprites
.getSize().y
+m_border_size
);
208 m_border_margins
[5]->setSize(QSize(2*m_border_size
+m_square_size
*m_sprites
.getSize().x
+2, 1));
210 m_border_margins
[2]->moveTo(-m_border_size
,0);
211 m_border_margins
[2]->setSize(QSize(m_border_size
, m_square_size
*m_sprites
.getSize().y
));
212 m_border_margins
[6]->moveTo(-m_border_size
-1,-m_border_size
);
213 m_border_margins
[6]->setSize(QSize(1, m_square_size
*m_sprites
.getSize().y
+2*m_border_size
));
215 m_border_margins
[3]->moveTo(m_square_size
*m_sprites
.getSize().x
, 0);
216 m_border_margins
[3]->setSize(QSize(m_border_size
, m_square_size
*m_sprites
.getSize().y
));
217 m_border_margins
[7]->moveTo(m_square_size
*m_sprites
.getSize().x
+m_border_size
, -m_border_size
);
218 m_border_margins
[7]->setSize(QSize(1, m_square_size
*m_sprites
.getSize().y
+2*m_border_size
));
221 void Board::createGrid(Point p
, const QStringList
& border_coords
) {
222 m_border_coords
= border_coords
;
223 m_sprites
= PieceGrid(p
.x
,p
.y
);
227 QRect
Board::computeRect(Point p
) const {
228 QPoint realPoint
= converter()->toReal(p
);
229 return squareRect(realPoint
.x(), realPoint
.y());
232 QRect
Board::squareRect(int x
, int y
) const {
233 return QRect(x
, y
, m_square_size
, m_square_size
);
236 QRegion
Board::computeRegion(Point p
) const {
237 return QRegion(computeRect(p
));
241 void Board::setSelection(const Point
& p
) {
242 lastSelection
= selection
;
244 setTags("selection", p
);
247 void Board::cancelSelection() {
248 lastSelection
= selection
;
249 selection
= Point::invalid();
250 clearTags("selection");
255 void Board::setPremove(const NormalUserMove
& premove
) {
256 m_premove_from
= premove
.from
;
257 m_premove_to
= premove
.to
;
258 setTags("premove", m_premove_from
, m_premove_to
);
261 void Board::setPremove(const DropUserMove
& premove
) {
262 m_premove_from
= Point::invalid();
263 m_premove_to
= premove
.m_to
;
264 setTags("premove", m_premove_to
);
267 void Board::setPremove(const Premove
& premove
) {
268 setPremove(premove
.toUserMove());
271 void Board::cancelPremove() {
272 m_premove_from
= Point::invalid();
273 m_premove_to
= Point::invalid();
274 clearTags("premove");
277 void Board::updateSprites() {
278 // adjust piece positions
279 for (Point i
= m_sprites
.first(); i
<= m_sprites
.last(); i
= m_sprites
.next(i
)) {
280 boost::shared_ptr
<Sprite
> p
= m_sprites
[i
].sprite();
284 p
->setPixmap( m_loader( m_sprites
[i
].name() ) );
290 void Board::updateTags() {
291 for(BoardTags::iterator tit
= m_tags
->begin(); tit
!= m_tags
->end(); ++tit
)
292 for(std::map
<Point
, boost::shared_ptr
<Canvas::Pixmap
> >::iterator pt
=
293 tit
->second
.begin(); pt
!= tit
->second
.end(); ++pt
) {
294 pt
->second
->moveTo(converter()->toReal(pt
->first
));
295 pt
->second
->setPixmap(m_tags_loader(tit
->first
));
300 bool Board::doMove(const NormalUserMove
& m
) {
301 if (m_entity
.lock()->oneClickMoves() || m_entity
.lock()->validTurn(m
.from
) == Moving
) {
302 AbstractMove::Ptr mv
= m_entity
.lock()->testMove(m
);
304 m_entity
.lock()->executeMove(mv
);
309 std::cout
<< "invalid move" << std::endl
;
310 emit
error(InvalidMove
);
316 void Board::onResize(int new_size
, bool force_reload
) {
317 if(m_square_size
== new_size
&& !force_reload
)
320 PieceGroup::onResize(new_size
);
322 // update the size of the tag loader
323 m_tags_loader
.setSize(m_square_size
);
325 // update canvas background
328 // update the sprites
338 void Board::onMousePress(const QPoint
& pos
, int button
) {
339 Point point
= converter()->toLogical(pos
);
340 if (!m_sprites
.valid(point
))
345 if (button
== Qt::LeftButton
) {
346 if (m_entity
.lock()->oneClickMoves()) {
347 NormalUserMove
m(Point::invalid(), point
);
348 m_entity
.lock()->setPromotion(m
);
352 shared_ptr
<Sprite
> piece
= m_sprites
[point
].sprite();
354 if (piece
&& m_entity
.lock()->movable(point
)) {
356 m_drag_info
= DragInfoPtr(new DragInfo(point
, pos
, piece
,
357 m_entity
.lock()->validTurn(point
)) );
361 // if selection is valid, (pre)move to point
362 else if (selection
!= Point::invalid()) {
363 piece
= m_sprites
[selection
].sprite();
365 NormalUserMove
m(selection
, point
);
366 m_entity
.lock()->setPromotion(m
);
368 switch(m_entity
.lock()->validTurn(selection
)) {
376 if (m_entity
.lock()->testPremove(m
)) {
377 m_entity
.lock()->addPremove(m
);
394 else if (button
== Qt::RightButton
) {
396 if (point
== m_premove_from
|| point
== m_premove_to
)
398 m_entity
.lock()->handleRightClick(point
);
404 void Board::onMouseRelease(const QPoint
& pos
, int button
) {
405 Point point
= converter()->toLogical(pos
);
409 if (button
== Qt::LeftButton
) {
412 // Q_ASSERT(m_drag_info->piece);
413 Q_ASSERT(m_drag_info
->sprite
);
416 // remove valid move highlighting
417 clearTags("validmove");
419 // toggle selection if the piece didn't move
420 if (m_drag_info
->from
== point
) {
421 if (lastSelection
== point
)
428 NormalUserMove
m(m_drag_info
->from
, point
, true);
429 if (!m_sprites
.valid(point
))
430 m
.to
= Point::invalid();
432 m_entity
.lock()->setPromotion(m
);
434 switch(m_entity
.lock()->validTurn(m_drag_info
->from
)) {
442 if (m_entity
.lock()->testPremove(m
)) {
443 m_entity
.lock()->addPremove(m
);
453 shared_ptr
<Sprite
> s
= m_sprites
[m_drag_info
->from
].sprite();
454 if (!moved
&& s
&& s
->pos() != converter()->toReal(m_drag_info
->from
)) {
456 QPoint real
= converter()->toReal(m_drag_info
->from
);
457 if( (point
== m_drag_info
->from
) ? !m_anim_movement
: !m_anim_fade
)
458 enqueue(shared_ptr
<Animation
>(new InstantAnimation(s
, real
)));
459 else if (point
== m_drag_info
->from
)
460 enqueue(shared_ptr
<Animation
>(new MovementAnimation(s
, real
)));
462 enqueue(shared_ptr
<Animation
>(new TeleportAnimation(s
, s
->pos(), real
)));
465 m_drag_info
= DragInfoPtr();
472 void Board::onMouseMove(const QPoint
& pos
, int /*button*/) {
473 Point point
= converter()->toLogical(pos
);
476 Q_ASSERT(m_drag_info
->sprite
);
477 // check drag threshold
478 if (!m_drag_info
->dragStarted
) {
479 QPoint delta
= pos
- m_drag_info
->real
;
480 if (delta
.x() * delta
.x() + delta
.y() * delta
.y() > DragInfo::DRAG_THRESHOLD
) {
481 m_drag_info
->dragStarted
= true;
484 if (m_drag_info
->dragStarted
)
485 m_drag_info
->sprite
->moveTo(pos
- QPoint(m_square_size
/ 2, m_square_size
/ 2) );
487 // highlight valid moves
488 NormalUserMove
move(m_drag_info
->from
, point
);
489 bool valid
= m_sprites
.valid(point
);
491 InteractionType action
= m_entity
.lock()->validTurn(m_drag_info
->from
);
492 if (action
== Moving
)
493 valid
= m_entity
.lock()->testMove(move
);
497 setTags("validmove", point
);
499 clearTags("validmove");
501 else if (m_entity
.lock()->oneClickMoves()) {
502 if(point
== m_hinting_pos
)
505 AbstractPiece::Ptr hint
;
507 if (m_sprites
.valid(point
)) {
508 if (AbstractMove::Ptr move
= m_entity
.lock()->testMove(
509 NormalUserMove(Point::invalid(), point
))) {
511 hint
= m_entity
.lock()->moveHint(move
);
515 updateHinting(point
, hint
);
519 void Board::onPositionChanged() {
520 if (m_entity
.lock() && m_entity
.lock()->oneClickMoves() && m_sprites
.valid(m_hinting_pos
)) {
521 AbstractPiece::Ptr hint
;
523 if (AbstractMove::Ptr move
= m_entity
.lock()->testMove(
524 NormalUserMove(Point::invalid(), m_hinting_pos
)) ) {
526 hint
= m_entity
.lock()->moveHint(move
);
529 updateHinting(m_hinting_pos
, hint
);
533 void Board::onMouseLeave() {
534 updateHinting(Point::invalid(), AbstractPiece::Ptr());
537 void Board::updateHinting(Point pt
, AbstractPiece::Ptr piece
) {
538 if(!m_sprites
.valid(pt
))
539 piece
= AbstractPiece::Ptr();
541 if(!piece
|| !m_sprites
.valid(pt
)) {
542 if(m_hinting
.sprite()) {
544 enqueue( boost::shared_ptr
<Animation
>(new FadeAnimation(m_hinting
.sprite(),
545 m_hinting
.sprite()->pos(), 160, 0)) );
547 enqueue( boost::shared_ptr
<Animation
>(new CaptureAnimation(m_hinting
.sprite())) );
550 m_hinting_pos
= Point::invalid();
551 m_hinting
= NamedSprite();
554 if(pt
== m_hinting_pos
) {
555 if(!(piece
->name() == m_hinting
.name())) {
556 m_hinting
= NamedSprite(piece
->name(), m_hinting
.sprite());
557 m_hinting
.sprite()->setPixmap(m_loader(piece
->name()));
561 if(m_hinting
.sprite()) {
563 enqueue( boost::shared_ptr
<Animation
>(new FadeAnimation(m_hinting
.sprite(),
564 m_hinting
.sprite()->pos(), 160, 0)) );
566 enqueue( boost::shared_ptr
<Animation
>(new CaptureAnimation(m_hinting
.sprite())) );
569 QPixmap pix
= m_loader(piece
->name());
570 boost::shared_ptr
<Sprite
> sprite
= createSprite(pix
, pt
);
571 sprite
->setOpacity(160);
576 m_hinting
= NamedSprite(piece
->name(), sprite
);
579 enqueue( boost::shared_ptr<Animation>(new FadeAnimation(m_hinting.sprite(),
580 m_hinting.sprite()->pos(), 0, 160)) );
582 m_hinting.sprite()->setOpacity(160);
583 enqueue(boost::shared_ptr<Animation>(new DropAnimation(m_hinting.sprite())) );
589 void Board::reset() {
593 m_main_animation
->stop();
596 void Board::flip(bool flipped
)
598 if (m_flipped
!= flipped
) {
601 // update sprite positions
602 for (Point i
= m_sprites
.first(); i
<= m_sprites
.last(); i
= m_sprites
.next(i
))
603 if (m_sprites
[i
].sprite())
604 animatePiece(m_sprites
[i
].sprite(), i
, 1.0);
611 void Board::draggingOn(int pool
, int index
, const QPoint
& point
) {
612 Point to
= converter()->toLogical(point
);
616 if (m_sprites
.valid(to
))
617 switch(m_entity
.lock()->validTurn(piece
->color())) {
619 DropUserMove
m(piece
, to
);
620 AbstractMove::Ptr mv
= m_entity
.lock()->testMove(m
);
622 setTags("validmove", to
);
629 setTags("validmove", to
);
637 clearTags("validmove");
640 bool Board::dropOn(int pool
, int index
, const QPoint
& point
) {
642 Point to
= converter()->toLogical(point
);
643 if (!m_sprites
.valid(to
))
646 clearTags("validmove");
650 switch(m_entity
.lock()->validTurn(piece
->color())) {
653 DropUserMove
m(piece
, to
);
654 AbstractMove::Ptr mv
= m_entity
.lock()->testMove(m
);
656 m_entity
.lock()->executeMove(mv
);
663 DropUserMove
m(piece
, to
);
664 if (m_entity
.lock()->testPremove(m
)) {
665 m_entity
.lock()->addPremove(m
);
675 std::cout
<< "invalid move" << std::endl
;
676 emit
error(InvalidMove
);