1 /***************************************************************************
2 BoardView - view of the current board
3 -------------------
4 begin : Sun 21 Aug 2005
5 copyright : (C) 2005-2007 Michal Rudolf <mrudolf@kdewebdev.org>
6 ***************************************************************************/
7
8 /***************************************************************************
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 ***************************************************************************/
16
17 #include "boardview.h"
18 #include "settings.h"
19 #include "guess.h"
20 #include "move.h"
21
22 #include <QApplication>
23 #include <QSizePolicy>
24
25 #if defined(_MSC_VER) && defined(_DEBUG)
26 #define DEBUG_NEW new( _NORMAL_BLOCK, __FILE__, __LINE__ )
27 #define new DEBUG_NEW
28 #endif // _MSC_VER
29
30 using namespace Qt;
31 using namespace chessx;
32
33 const int CoordinateSize = 16;
34 const int MoveIndicatorSize = 12;
35
BoardView(QWidget * parent,int flags)36 BoardView::BoardView(QWidget* parent, int flags) : QWidget(parent),
37 m_flipped(false), m_showFrame(false), m_showCurrentMove(2),
38 m_guessMove(false), m_showThreat(false), m_showTargets(false), m_brushMode(false), m_selectedSquare(InvalidSquare),
39 m_hoverSquare(InvalidSquare),
40 m_hiFrom(InvalidSquare), m_hiTo(InvalidSquare),
41 m_currentFrom(InvalidSquare), m_currentTo(InvalidSquare),
42 m_storedFrom(InvalidSquare), m_storedTo(InvalidSquare),
43 m_dragStartSquare(InvalidSquare),
44 m_atLineEnd(true),
45 m_flags(flags),
46 m_coordinates(false), m_dragged(Empty), m_clickUsed(false), m_wheelCurrentDelta(0),
47 m_minDeltaWheel(0), m_moveListCurrent(0), m_showMoveIndicator(true), m_showMoveIndicatorMode(0),
48 m_DbIndex(nullptr),
49 m_showAttacks(NoColor),
50 m_showUnderProtection(NoColor),
51 lastMoveEvent(nullptr)
52 {
53 QSizePolicy policy = sizePolicy();
54 policy.setHeightForWidth(true);
55 setSizePolicy(policy);
56 setMouseTracking(true);
57 installEventFilter(this);
58 setFocusPolicy( Qt::StrongFocus );
59
60 m_bestGuess.setNullMove();
61 m_threatGuess.setThinkTime(500);
62
63 connect(&m_threatGuess, SIGNAL(guessFoundForBoard(Guess::Result, BoardX)),
64 this, SLOT(showThreat(Guess::Result,BoardX)), Qt::QueuedConnection);
65 }
66
~BoardView()67 BoardView::~BoardView()
68 {
69 removeEventFilter(this);
70 m_threatGuess.cancel();
71 delete lastMoveEvent;
72 }
73
heightForWidth(int width) const74 int BoardView::heightForWidth(int width) const
75 {
76 int coord = m_coordinates ? CoordinateSize : 0;
77 int moveIndicator = (m_showMoveIndicatorMode==Always) ? MoveIndicatorSize:0;
78 int xsize = (width - 1 - coord - moveIndicator) / 8;
79
80 int square = moveIndicatorWidth(width, xsize);
81 return (width - square);
82 }
83
eventFilter(QObject * obj,QEvent * ev)84 bool BoardView::eventFilter(QObject *obj, QEvent *ev)
85 {
86 if(ev->type() == QEvent::Leave || ev->type() == QEvent::WindowDeactivate)
87 {
88 removeGuess();
89 }
90 return QWidget::eventFilter(obj, ev);
91 }
92
setFlags(int flags)93 void BoardView::setFlags(int flags)
94 {
95 m_flags = flags;
96 }
97
flags() const98 int BoardView::flags() const
99 {
100 return m_flags;
101 }
102
setBoard(const BoardX & value,Square from,Square to,bool atLineEnd)103 void BoardView::setBoard(const BoardX& value, Square from, Square to, bool atLineEnd)
104 {
105 m_clickUsed = true;
106 m_board = value;
107 m_currentFrom = from;
108 m_currentTo = to;
109 m_atLineEnd = atLineEnd;
110 m_hiFrom = m_hiTo = InvalidSquare;
111 m_alertSquare = value.kingInCheck();
112 m_targets.clear();
113 m_bestGuess.setNullMove();
114 if(underMouse())
115 {
116 updateGuess(m_hoverSquare);
117 }
118 updateThreat();
119 repaint();
120 }
121
setStoredMove(Square from,Square to)122 void BoardView::setStoredMove(Square from, Square to)
123 {
124 m_storedFrom = from;
125 m_storedTo = to;
126 update();
127 }
128
getStoredMove(Square & from,Square & to)129 void BoardView::getStoredMove(Square& from, Square& to)
130 {
131 from = m_storedFrom;
132 to = m_storedTo;
133 m_storedFrom = InvalidSquare;
134 m_storedTo = InvalidSquare;
135 update();
136 }
137
board() const138 BoardX BoardView::board() const
139 {
140 return m_board;
141 }
142
theme() const143 const BoardTheme& BoardView::theme() const
144 {
145 return m_theme;
146 }
147
themeSize() const148 QSize BoardView::themeSize() const
149 {
150 return m_theme.size();
151 }
152
showMoveIndicator(bool visible)153 void BoardView::showMoveIndicator(bool visible)
154 {
155 m_showMoveIndicator = visible;
156 }
157
showMoveIndicator() const158 bool BoardView::showMoveIndicator() const
159 {
160 return m_showMoveIndicator;
161 }
162
showCoordinates(bool visible)163 void BoardView::showCoordinates(bool visible)
164 {
165 m_coordinates = visible;
166 }
167
showCoordinates() const168 bool BoardView::showCoordinates() const
169 {
170 return m_coordinates;
171 }
172
drawSquares(QPaintEvent * event)173 void BoardView::drawSquares(QPaintEvent* event)
174 {
175 QPainter p(this);
176 for (Square square=a1; square<NumSquares; ++square)
177 {
178 QRect rect = squareRect(square);
179 if(!event->region().intersects(rect))
180 {
181 continue;
182 }
183 int coord = m_coordinates ? CoordinateSize : 0;
184 int x = isFlipped() ? 7 - square % 8 : square % 8;
185 int y = isFlipped() ? square / 8 : 7 - square / 8;
186 QPoint pos(coord + x * m_theme.size().width(), y * m_theme.size().height());
187 p.drawPixmap(pos, m_theme.square((x + y) % 2));
188 }
189 }
190
totalRect() const191 QRect BoardView::totalRect() const
192 {
193 QRect rect1 = squareRect(isFlipped() ? h1 : a8);
194 QRect rect2;
195 if (m_coordinates)
196 {
197 rect2 = coordinateRectHorizontal(0);
198 }
199 else
200 {
201 rect2 = squareRect(isFlipped() ? a8 : h1);
202 }
203 QRect totalRect = QRect(0,rect1.top(), width(), rect2.bottom()+2);
204 return totalRect;
205 }
206
drawCoordinates(QPaintEvent * event)207 void BoardView::drawCoordinates(QPaintEvent* event)
208 {
209 if(m_coordinates)
210 {
211 QPainter p(this);
212 p.save();
213 p.setPen(m_theme.color(BoardTheme::Frame));
214 for(int i = 0; i<8; ++i)
215 {
216 QRect rect = coordinateRectVertical(i);
217 if(!event->region().intersects(rect))
218 {
219 continue;
220 }
221 p.drawText(rect, Qt::AlignCenter, QString("%1").arg(i + 1));
222 }
223 for(int i = 0; i<8; ++i)
224 {
225 QRect rect = coordinateRectHorizontal(i);
226 if(!event->region().intersects(rect))
227 {
228 continue;
229 }
230 p.drawText(rect, Qt::AlignCenter, QString("%1").arg(QChar('a' + i)));
231 }
232 p.restore();
233 }
234 }
235
drawDraggedPieces(QPaintEvent *)236 void BoardView::drawDraggedPieces(QPaintEvent* /*event*/)
237 {
238 // Draw dragged piece
239 if(m_dragged != Empty)
240 {
241 QPainter p(this);
242 p.drawPixmap(m_dragPoint, m_theme.piece(m_dragged));
243 }
244 }
245
drawMoveIndicator(QPaintEvent * event)246 void BoardView::drawMoveIndicator(QPaintEvent* event)
247 {
248 if(m_showMoveIndicator && m_showMoveIndicatorMode!=Never)
249 {
250 QPainter p(this);
251 // Draw side to move indicator
252 int coord = m_coordinates ? CoordinateSize : 0;
253 bool white = m_board.toMove() == White;
254 int square = moveIndicatorWidth(width(), m_theme.size().width());
255 if(square)
256 {
257 int posy = (white == m_flipped) ? 1 : 8 * m_theme.size().width() - square;
258 QRect rect(QPoint(8 * m_theme.size().width() + 2 + coord, posy),
259 QSize(square, square));
260 if(!event->region().intersects(rect))
261 {
262 return;
263 }
264 QColor color = white ? Qt::white : Qt::black;
265 QColor border = white ? Qt::black : Qt::gray;
266 p.setPen(border);
267 p.setBrush(QColor(color));
268 p.drawRect(rect);
269 }
270 }
271 }
272
drawHiliteSquare(QPoint pos,BoardTheme::ColorRole role)273 void BoardView::drawHiliteSquare(QPoint pos, BoardTheme::ColorRole role)
274 {
275 QPainter p(this);
276
277 QPen pen;
278 pen.setColor(m_theme.color(role));
279 pen.setWidth(2);
280 pen.setJoinStyle(Qt::MiterJoin);
281 p.setPen(pen);
282 p.drawRect(pos.x() + 1 + m_showFrame, pos.y() + 1 + m_showFrame,
283 m_theme.size().width() - 2 - m_showFrame, m_theme.size().height() - 2 - m_showFrame);
284 }
285
drawHiliting(QPaintEvent * event)286 void BoardView::drawHiliting(QPaintEvent* event)
287 {
288 for (Square square=a1; square<NumSquares; ++square)
289 {
290 QRect rect = squareRect(square);
291 if(!event->region().intersects(rect))
292 {
293 continue;
294 }
295
296 QPoint pos = posFromSquare(square);
297
298 if(square == m_storedFrom || square == m_storedTo)
299 {
300 drawHiliteSquare(pos, BoardTheme::StoredMove);
301 }
302
303 if(m_showCurrentMove==1)
304 {
305 if(square == m_currentFrom || square == m_currentTo)
306 {
307 drawHiliteSquare(pos, BoardTheme::CurrentMove);
308 }
309 }
310
311 if(square == m_selectedSquare || square == m_hiFrom || square == m_hiTo)
312 {
313 drawHiliteSquare(pos, BoardTheme::Highlight);
314 }
315 }
316
317 if ((m_showCurrentMove==2) && m_currentFrom != InvalidSquare && m_currentTo != InvalidSquare)
318 {
319 QRect rect1 = squareRect(m_currentFrom);
320 QRect rect2 = squareRect(m_currentTo);
321 QRect u = rect1.united(rect2);
322 if(event->region().intersects(u))
323 {
324 drawArrow(m_currentFrom, m_currentTo, m_theme.color(BoardTheme::CurrentMove));
325 }
326 }
327
328 Square threatFrom = m_threatGuess.getFrom();
329 Square threatTo = m_threatGuess.getTo();
330 if (threatFrom != InvalidSquare && threatTo != InvalidSquare)
331 {
332 QRect rect1 = squareRect(threatFrom);
333 QRect rect2 = squareRect(threatTo);
334 QRect u = rect1.united(rect2);
335 if(event->region().intersects(u))
336 {
337 drawArrow(threatFrom, threatTo, m_theme.color(BoardTheme::Threat));
338 }
339 }
340
341 if (!m_bestGuess.isNullMove() && m_showThreat)
342 {
343 Square guessFrom = m_bestGuess.from();
344 Square guessTo = m_bestGuess.to();
345 if (guessFrom != InvalidSquare && guessTo != InvalidSquare)
346 {
347 QRect rect1 = squareRect(guessFrom);
348 QRect rect2 = squareRect(guessTo);
349 QRect u = rect1.united(rect2);
350 if(event->region().intersects(u))
351 {
352 QColor c(m_theme.color(BoardTheme::Engine));
353 drawArrow(guessFrom, guessTo, c);
354 }
355 }
356 }
357
358 // draw variation arrows (whether they are really displayed depends on the contents of m_variations)
359 int thin = 0;
360 foreach(Move move, m_variations)
361 {
362 Square moveFrom = move.from();
363 Square moveTo = move.to();
364 QRect rect1 = squareRect(moveFrom);
365 QRect rect2 = squareRect(moveTo);
366 QRect u = rect1.united(rect2);
367 if(event->region().intersects(u))
368 {
369 QColor c(m_theme.color(BoardTheme::VariationMove));
370 drawArrow(moveFrom, moveTo, c, thin);
371 }
372 thin++; // all but the first variation are drawn as thin
373 }
374 }
375
drawTargets(QPaintEvent * event)376 void BoardView::drawTargets(QPaintEvent* event)
377 {
378 for (Square square=a1; square<NumSquares; ++square)
379 {
380 if (m_targets.contains(square))
381 {
382 drawColorRect(event, square, m_theme.color(BoardTheme::Target));
383 }
384 }
385 }
386
drawCheck(QPaintEvent * event)387 void BoardView::drawCheck(QPaintEvent* event)
388 {
389 if (isEnabled())
390 {
391 QPainter p(this);
392 drawColorRect(event, m_alertSquare, m_theme.color(BoardTheme::Check));
393 }
394 }
395
drawAttacks(QPaintEvent * event)396 void BoardView::drawAttacks(QPaintEvent* event)
397 {
398 if (isEnabled() && Guess::guessAllowed())
399 {
400 if (m_showAttacks != NoColor)
401 {
402 QPainter p(this);
403 for (Square square=a1; square<NumSquares; ++square)
404 {
405 if (m_board.isAttackedBy(m_showAttacks, square))
406 {
407 drawColorRect(event, square, m_theme.color(BoardTheme::Wall), true);
408 }
409 }
410 }
411 }
412 }
413
drawUnderProtection(QPaintEvent * event)414 void BoardView::drawUnderProtection(QPaintEvent* event)
415 {
416 if (isEnabled() && Guess::guessAllowed())
417 {
418 if (m_showUnderProtection != NoColor)
419 {
420 QPainter p(this);
421 for (Square square=a1; square<NumSquares; ++square)
422 {
423 if (m_board.colorAt(square) == m_showUnderProtection)
424 {
425 if (pieceType(m_board.pieceAt(square)) != King)
426 {
427 int numDefenders = m_board.DefendersOfSquare(square);
428 if ((m_showUnderProtection == White && numDefenders < 0) ||
429 (m_showUnderProtection == Black && numDefenders > 0))
430 {
431 drawColorRect(event, square, m_theme.color(BoardTheme::UnderProtected), true);
432 }
433 }
434 }
435 }
436 }
437 }
438 }
439
440
441
drawPieces(QPaintEvent * event)442 void BoardView::drawPieces(QPaintEvent* event)
443 {
444 QPainter p(this);
445 p.setRenderHint(QPainter::SmoothPixmapTransform);
446 for (Square square=a1; square<NumSquares; ++square)
447 {
448 QRect rect = squareRect(square);
449 if(!event->region().intersects(rect))
450 {
451 continue;
452 }
453
454 QPoint pos = posFromSquare(square);
455
456 if(m_showFrame)
457 {
458 p.setPen(m_theme.color(BoardTheme::Frame));
459 p.drawRect(QRect(pos, m_theme.size()));
460 }
461
462 if(m_dragged != Empty)
463 {
464 // Do not paint piece that is dragged
465 if (m_dragStartSquare == square)
466 {
467 continue;
468 }
469 }
470
471 p.drawPixmap(pos, m_theme.piece(m_board.pieceAt(square)));
472 }
473
474 // Always draw outer rectangle
475 QRect rect1 = squareRect(a8);
476 QRect rect2 = squareRect(h1);
477 p.setPen(m_theme.color(BoardTheme::Frame));
478 p.drawRect(!isFlipped() ? QRect(rect1.topLeft(),rect2.bottomRight()) :
479 QRect(rect2.topLeft(),rect1.bottomRight()));
480 }
481
paintEvent(QPaintEvent * event)482 void BoardView::paintEvent(QPaintEvent* event)
483 {
484 drawSquares(event);
485 drawCoordinates(event);
486 drawAttacks(event);
487 drawSquareAnnotations(event);
488 drawTargets(event);
489 drawCheck(event);
490 drawUnderProtection(event);
491 drawPieces(event);
492 drawHiliting(event);
493 drawMoveIndicator(event);
494 drawArrowAnnotations(event);
495 drawDraggedPieces(event);
496 }
497
resizeBoard(QSize sz)498 void BoardView::resizeBoard(QSize sz)
499 {
500 // subtract move indicator from width
501 int coord = m_coordinates ? CoordinateSize : 0;
502 int moveIndicator = (m_showMoveIndicatorMode==Always) ? MoveIndicatorSize:0;
503 int xsize = (sz.width() - 1 - coord - moveIndicator) / 8;
504 int ysize = (sz.height() - 1 - coord) / 8;
505 int size = std::min(xsize, ysize);
506 m_theme.setSize(QSize(size, size));
507
508 // calculate the translation vector to center the widget inside its parent
509 int square = moveIndicatorWidth(width(), m_theme.size().width());
510
511 QSize widgetSize(size * 8 + 1 + coord + square, size * 8 + 1 + coord);
512 QPoint widgetCenter(widgetSize.width() / 2, widgetSize.height() / 2);
513 }
514
resizeEvent(QResizeEvent * e)515 void BoardView::resizeEvent(QResizeEvent* e)
516 {
517 resizeBoard(e->size());
518 QWidget::resizeEvent(e);
519 }
520
sizeHint() const521 QSize BoardView::sizeHint() const
522 {
523 int w = geometry().width();
524 int h = heightForWidth(w);
525 return QSize(w,h);
526 }
527
moveIndicatorWidth(int width,int themeWidth) const528 int BoardView::moveIndicatorWidth(int width, int themeWidth) const
529 {
530 if(m_showMoveIndicator && m_showMoveIndicatorMode!=Never)
531 {
532 int coord = m_coordinates ? CoordinateSize : 0;
533 int square = width - coord - 8 * themeWidth - 4;
534 int maxsquare = themeWidth / 2;
535 if (square > maxsquare)
536 {
537 square = maxsquare;
538 }
539 if (square <= 8)
540 {
541 square = 0;
542 }
543 return square;
544 }
545 return 0;
546 }
547
548
squareAt(const QPoint & p) const549 Square BoardView::squareAt(const QPoint& p) const
550 {
551 int x = p.x();
552 int y = p.y();
553 int width = m_theme.size().width();
554 int height = m_theme.size().height();
555 x -= m_coordinates ? CoordinateSize : 0;
556 if(x <= 0 || y <= 0 || x >= width * 8 || y >= height * 8)
557 {
558 return InvalidSquare;
559 }
560 x /= width;
561 y /= height;
562 return Square(isFlipped() ? (8 * y + 7 - x) : (8 * (7 - y) + x));
563 }
564
mousePressEvent(QMouseEvent * event)565 void BoardView::mousePressEvent(QMouseEvent* event)
566 {
567 m_dragStart = event->pos();
568 setStoredMove(InvalidSquare,InvalidSquare);
569 }
570
showGuess(Square s)571 bool BoardView::showGuess(Square s)
572 {
573 // Don't want to constantly recalculate guess, so remember which square
574 // the mouse is hovering over, and only show new guess when it changes
575 if((m_guessMove || m_showTargets) && s != InvalidSquare && s != m_hoverSquare && !(m_flags & SuppressGuessMove))
576 {
577 removeGuess();
578 m_hoverSquare = s;
579
580 if (s != InvalidSquare)
581 {
582 Guess::Result sm = Guess::guessMove(qPrintable(m_board.toFen()), m_board.chess960(), m_board.castlingRooks(),
583 static_cast<Guess::squareT>(s), m_moveList);
584 if(!sm.error)
585 {
586 if (m_guessMove)
587 {
588 m_hiFrom = Square(sm.from);
589 m_hiTo = Square(sm.to);
590 update(squareRect(m_hiFrom));
591 update(squareRect(m_hiTo));
592 }
593
594 if (m_showTargets)
595 {
596 foreach(Guess::simpleMoveT sm, m_moveList)
597 {
598 if (s == Square(sm.from))
599 {
600 Square target = Square(sm.visualTo());
601 m_targets.append(target);
602 update(squareRect(target));
603 }
604 }
605 }
606 }
607 }
608 return true;
609 }
610 return false;
611 }
612
updateGuess(Square s)613 void BoardView::updateGuess(Square s)
614 {
615 // Invalidate any currently displayed guess to allow new guess to show
616 m_hoverSquare = InvalidSquare;
617 showGuess(s);
618 }
619
updateThreat()620 void BoardView::updateThreat()
621 {
622 m_threatGuess.clear();
623 if(m_showThreat && !(m_flags & SuppressGuessMove) && Guess::guessAllowed())
624 {
625 if (board() != BoardX::standardStartBoard)
626 {
627 m_threatGuess.guessMove(board());
628 }
629 }
630 }
631
showThreat(Guess::Result sm,BoardX b)632 void BoardView::showThreat(Guess::Result sm, BoardX b)
633 {
634 if (board() == b)
635 {
636 if(!sm.error)
637 {
638 update();
639 }
640 }
641 else
642 {
643 ThreadedGuess* guessEngine = qobject_cast<ThreadedGuess*>(sender());
644 if (guessEngine)
645 {
646 guessEngine->guessMove(board());
647 }
648 }
649 }
650
removeGuess()651 void BoardView::removeGuess()
652 {
653 if(m_hiFrom != InvalidSquare)
654 {
655 update(squareRect(m_hiFrom));
656 update(squareRect(m_hiTo));
657 m_hiFrom = m_hiTo = InvalidSquare;
658 }
659 m_hoverSquare = InvalidSquare;
660 m_moveListCurrent = 0;
661 m_moveList.clear();
662 foreach(Square s, m_targets)
663 {
664 update(squareRect(s));
665 }
666 m_targets.clear();
667 }
668
nextGuess(Square s)669 void BoardView::nextGuess(Square s)
670 {
671 if(!showGuess(s))
672 {
673 if(m_moveList.size() && (int) m_moveListCurrent < m_moveList.size())
674 {
675 Guess::simpleMoveT * sold = m_moveList.Get(m_moveListCurrent);
676 update(squareRect(Square(sold->from)));
677 update(squareRect(Square(sold->visualTo())));
678
679 if((int)m_moveListCurrent < m_moveList.size() - 1)
680 {
681 ++m_moveListCurrent;
682 }
683 else
684 {
685 m_moveListCurrent = 0;
686 }
687
688 Guess::simpleMoveT * sm = m_moveList.Get(m_moveListCurrent);
689 m_hiFrom = Square(sm->from);
690 m_hiTo = Square(sm->visualTo());
691 update(squareRect(m_hiFrom));
692 update(squareRect(m_hiTo));
693 }
694 }
695 }
696
moveActionFromModifier(Qt::KeyboardModifiers modifiers) const697 BoardView::BoardViewAction BoardView::moveActionFromModifier(Qt::KeyboardModifiers modifiers) const
698 {
699 switch (modifiers & 0x7e000000)
700 {
701 case ShiftModifier:
702 return ActionPen;
703 case ControlModifier:
704 return m_atLineEnd ? ActionQuery : ActionReplace;
705 case AltModifier:
706 return m_atLineEnd ? ActionStandard : ActionAdd;
707 case (unsigned int)ControlModifier | (unsigned int)AltModifier:
708 return m_atLineEnd ? ActionStandard : ActionInsert;
709 case (unsigned int)ControlModifier | (unsigned int)ShiftModifier:
710 return ActionAskEngine;
711 case (unsigned int)ShiftModifier | (unsigned int)AltModifier:
712 return ActionEvalMove;
713 default:
714 return ActionStandard;
715 }
716 }
717
checkCursor(Qt::KeyboardModifiers modifiers)718 void BoardView::checkCursor(Qt::KeyboardModifiers modifiers)
719 {
720 const char* file = nullptr;
721 QString text;
722
723 if (underMouse())
724 {
725 if (m_brushMode)
726 {
727 if (modifiers & AltModifier)
728 {
729 file = ":/images/spray.png";
730 text = tr("Spray color annotations");
731 }
732 else
733 {
734 file = ":/images/pen.png";
735 text = tr("Draw a square or arrow annotation");
736 }
737 }
738 else switch (moveActionFromModifier(modifiers))
739 {
740 case ActionStandard:
741 break;
742 case ActionQuery:
743 file = ":/images/query_move.png";
744 text = tr("Query for piece in case of promotion");
745 break;
746 case ActionReplace:
747 file = ":/images/replace_move.png";
748 text = tr("Replace remainder of game with new move");
749 break;
750 case ActionInsert:
751 file = ":/images/insert_move.png";
752 text = tr("Insert new move and keep as much as possible of remaining moves");
753 break;
754 case ActionAdd:
755 file = ":/images/plus.png";
756 text = tr("Force adding a variation");
757 break;
758 case ActionPen:
759 file = ":/images/pen.png";
760 text = tr("Draw a square or arrow annotation");
761 break;
762 case ActionAskEngine:
763 file = ":/images/engine.png";
764 text = tr("Query the engine as if piece was located at target");
765 break;
766 case ActionEvalMove:
767 file = ":/images/engine.png";
768 text = tr("Query the engine for the best reply");
769 break;
770 }
771 }
772
773 if(file)
774 {
775 setCursor(QCursor(QPixmap(file)));
776 }
777 else
778 {
779 setCursor(QCursor(Qt::ArrowCursor));
780 }
781
782 emit actionHint(text);
783
784 if ((m_dragged != Empty) && (lastMoveEvent))
785 {
786 lastMoveEvent->setModifiers(modifiers);
787 handleMouseMoveEvent(lastMoveEvent);
788 }
789 }
790
keyPressEvent(QKeyEvent * event)791 void BoardView::keyPressEvent(QKeyEvent *event)
792 {
793 checkCursor(QApplication::queryKeyboardModifiers());
794 QWidget::keyPressEvent(event);
795 }
796
keyReleaseEvent(QKeyEvent * event)797 void BoardView::keyReleaseEvent(QKeyEvent *event)
798 {
799 checkCursor(QApplication::queryKeyboardModifiers());
800 QWidget::keyReleaseEvent(event);
801 }
802
enterEvent(QEvent * event)803 void BoardView::enterEvent(QEvent *event)
804 {
805 setFocus();
806 raise();
807 checkCursor(QApplication::queryKeyboardModifiers());
808 QWidget::enterEvent(event);
809 }
810
leaveEvent(QEvent * event)811 void BoardView::leaveEvent(QEvent *event)
812 {
813 emit actionHint("");
814 QWidget::leaveEvent(event);
815 }
816
handleMouseMoveEvent(QMouseEvent * event)817 void BoardView::handleMouseMoveEvent(QMouseEvent *event)
818 {
819 setFocus();
820
821 Qt::KeyboardModifiers mdf = event->modifiers();
822 auto b = event->buttons();
823 if(!(b & Qt::LeftButton))
824 {
825 if (!m_brushMode)
826 {
827 if(!(mdf & Qt::ShiftModifier))
828 {
829 showGuess(squareAt(event->pos()));
830 }
831 else
832 {
833 removeGuess();
834 }
835 }
836 return;
837 }
838
839 if (m_brushMode && (b & Qt::LeftButton) && (mdf & Qt::AltModifier))
840 {
841 Square s = squareAt(event->pos());
842 if ((m_dragStartSquare == InvalidSquare) || (m_dragStartSquare != s))
843 {
844 emit clicked(s, b, mapToGlobal(event->pos()), s);
845 m_dragStart = event->pos();
846 m_dragStartSquare = s;
847 return;
848 }
849 }
850
851 if(m_dragged != Empty)
852 {
853 QRect old = QRect(m_dragPoint, m_theme.size());
854 m_dragPoint = event->pos() - m_theme.pieceCenter();
855 update(old);
856 update(QRect(m_dragPoint, m_theme.size()));
857 if (moveActionFromModifier(mdf) == ActionAskEngine)
858 {
859 emit evalRequest(m_dragStartSquare, squareAt(event->pos()));
860 }
861 else if (moveActionFromModifier(mdf) == ActionEvalMove)
862 {
863 emit evalMove(m_dragStartSquare, squareAt(event->pos()));
864 }
865 return;
866 }
867
868 if(mdf & Qt::ShiftModifier && (moveActionFromModifier(mdf) != ActionAskEngine) && (moveActionFromModifier(mdf) != ActionEvalMove))
869 {
870 return;
871 }
872
873 if((event->pos() - m_dragStart).manhattanLength()
874 < QApplication::startDragDistance())
875 {
876 // Click and move - start dragging
877 return;
878 }
879
880 Square s = squareAt(m_dragStart);
881 if(!canDrag(s, mdf))
882 {
883 return;
884 }
885
886 startToDrag(event, s);
887 }
888
mouseMoveEvent(QMouseEvent * event)889 void BoardView::mouseMoveEvent(QMouseEvent *event)
890 {
891 delete lastMoveEvent;
892 lastMoveEvent = new QMouseEvent(*event);
893
894 handleMouseMoveEvent(event);
895 QWidget::mouseMoveEvent(event);
896 }
897
startToDrag(QMouseEvent * event,Square s)898 void BoardView::startToDrag(QMouseEvent *event, Square s)
899 {
900 removeGuess();
901 if (!m_brushMode) m_dragged = m_board.pieceAt(s);
902 m_dragPoint = event->pos() - m_theme.pieceCenter();
903 m_dragStartSquare = s;
904 update(squareRect(s));
905 update(QRect(m_dragPoint, m_theme.size()));
906 unselectSquare();
907 }
908
getBestGuess() const909 Move BoardView::getBestGuess() const
910 {
911 return m_bestGuess;
912 }
913
setBestGuess(const Move & bestGuess)914 void BoardView::setBestGuess(const Move &bestGuess)
915 {
916 m_bestGuess = bestGuess;
917 update();
918 }
919
setVariations(const QList<Move> & variations)920 void BoardView::setVariations(const QList<Move> &variations)
921 {
922 m_variations = variations;
923 update();
924 }
925
setShowUnderProtection(const Color & showUnderProtection)926 void BoardView::setShowUnderProtection(const Color &showUnderProtection)
927 {
928 m_showUnderProtection = showUnderProtection;
929 update();
930 }
931
setShowAttacks(const Color & showAttacks)932 void BoardView::setShowAttacks(const Color &showAttacks)
933 {
934 m_showAttacks = showAttacks;
935 update();
936 }
937
getBrushMode() const938 bool BoardView::getBrushMode() const
939 {
940 return m_brushMode;
941 }
942
setBrushMode(bool brushMode)943 void BoardView::setBrushMode(bool brushMode)
944 {
945 m_brushMode = brushMode;
946 }
947
mouseReleaseEvent(QMouseEvent * event)948 void BoardView::mouseReleaseEvent(QMouseEvent* event)
949 {
950 delete lastMoveEvent;
951 lastMoveEvent = nullptr;
952 int button = event->button() + event->modifiers();
953 Square s = squareAt(event->pos());
954 m_clickUsed = false;
955 Square from = squareAt(m_dragStart);
956
957 if (!canDrop(from))
958 {
959 if(m_dragged != Empty)
960 {
961 m_dragStartSquare = InvalidSquare;
962 QRect oldr = QRect(m_dragPoint, m_theme.size());
963 m_dragged = Empty;
964 update(squareRect(from));
965 update(oldr);
966 emit evalModeDone();
967 return;
968 }
969 }
970
971 if(!(event->button() & Qt::LeftButton))
972 {
973 if(s == from)
974 {
975 from = InvalidSquare;
976 }
977 if(s != InvalidSquare)
978 {
979 emit clicked(s, button, mapToGlobal(event->pos()), from);
980 }
981 else
982 {
983 Square from = squareAt(m_dragStart);
984 emit invalidMove(from);
985 }
986 m_dragged = Empty;
987 return;
988 }
989 else
990 {
991 if ((moveActionFromModifier(event->modifiers()) == ActionAskEngine) || (moveActionFromModifier(event->modifiers()) == ActionEvalMove))
992 {
993 m_dragStartSquare = InvalidSquare;
994 QRect oldr = QRect(m_dragPoint, m_theme.size());
995 m_dragged = Empty;
996 update(squareRect(from));
997 update(oldr);
998 emit evalModeDone();
999 return;
1000 }
1001 if(event->modifiers() & Qt::ShiftModifier)
1002 {
1003 if(s != InvalidSquare)
1004 {
1005 emit clicked(s, button, mapToGlobal(event->pos()), from);
1006 }
1007 if (m_dragged != Empty)
1008 {
1009 update(squareRect(from));
1010 QRect oldr = QRect(m_dragPoint, m_theme.size());
1011 update(oldr);
1012 m_dragged = Empty;
1013 }
1014 return;
1015 }
1016 }
1017
1018 if(m_brushMode)
1019 {
1020 m_dragStartSquare = InvalidSquare;
1021 if ((s != InvalidSquare) && !(event->modifiers() & Qt::AltModifier))
1022 {
1023 emit clicked(s, button, mapToGlobal(event->pos()), from);
1024 m_dragged = Empty;
1025 }
1026 }
1027 else if(m_dragged != Empty)
1028 {
1029 m_dragStartSquare = InvalidSquare;
1030 QRect oldr = QRect(m_dragPoint, m_theme.size());
1031 m_dragged = Empty;
1032 update(squareRect(from));
1033 update(oldr);
1034 if(s != InvalidSquare)
1035 {
1036 if((m_flags & AllowCopyPiece) && (event->modifiers() & Qt::AltModifier))
1037 {
1038 if(m_board.pieceAt(from) != Empty)
1039 {
1040 emit copyPiece(from, s);
1041 }
1042 }
1043 else
1044 {
1045 updateGuess(s);
1046 emit moveMade(from, s, button);
1047 }
1048 }
1049 else
1050 {
1051 emit invalidMove(from);
1052 }
1053 }
1054 else if(m_selectedSquare != InvalidSquare)
1055 {
1056 Square from = m_selectedSquare;
1057 unselectSquare();
1058 if(s != InvalidSquare)
1059 {
1060 emit moveMade(from, s, button);
1061 }
1062 }
1063 else if(m_hiFrom != InvalidSquare)
1064 {
1065 if(s == m_hiFrom || s == m_hiTo)
1066 {
1067 emit moveMade(m_hiFrom, m_hiTo, button);
1068 }
1069 m_hoverSquare = InvalidSquare;
1070 // Only update guess if "emit moveMade()" did not pop up a window (eg. promotion)
1071 if(m_hiFrom != InvalidSquare)
1072 {
1073 updateGuess(s);
1074 }
1075 }
1076 else
1077 {
1078 if(s != InvalidSquare)
1079 {
1080 emit clicked(s, button, mapToGlobal(event->pos()), InvalidSquare);
1081 if(!m_clickUsed && m_board.isMovable(s))
1082 {
1083 selectSquare(s);
1084 }
1085 }
1086 }
1087 m_dragged = Empty;
1088 QWidget::mouseReleaseEvent(event);
1089 }
1090
wheelEvent(QWheelEvent * e)1091 void BoardView::wheelEvent(QWheelEvent* e)
1092 {
1093 m_wheelCurrentDelta += e->delta();
1094 if(abs(m_wheelCurrentDelta) > m_minDeltaWheel)
1095 {
1096 int change = m_wheelCurrentDelta < 0 ? WheelDown : WheelUp;
1097 emit wheelScrolled(change + e->modifiers());
1098 m_wheelCurrentDelta = 0;
1099 }
1100 QWidget::wheelEvent(e);
1101 }
1102
setFlipped(bool flipped)1103 void BoardView::setFlipped(bool flipped)
1104 {
1105 bool wasFlipped = m_flipped;
1106 m_flipped = flipped;
1107 repaint(); // Workaround Bug in Qt at least up to version 5.12
1108 emit signalFlipped(wasFlipped, m_flipped);
1109 }
1110
flip()1111 void BoardView::flip()
1112 {
1113 setFlipped(!m_flipped);
1114 }
1115
isFlipped() const1116 bool BoardView::isFlipped() const
1117 {
1118 return m_flipped;
1119 }
1120
configure()1121 void BoardView::configure()
1122 {
1123 AppSettings->beginGroup("/Board/");
1124 m_showFrame = AppSettings->getValue("showFrame").toBool();
1125 m_coordinates = AppSettings->getValue("showCoordinates").toBool();
1126 m_showCurrentMove = AppSettings->getValue("showCurrentMove").toInt();
1127 m_guessMove = AppSettings->getValue("guessMove").toBool();
1128 m_showTargets = AppSettings->getValue("showTargets").toBool();
1129 m_showThreat = AppSettings->getValue("showThreat").toBool();
1130 m_minDeltaWheel = AppSettings->getValue("minWheelCount").toInt();
1131 m_showMoveIndicatorMode = AppSettings->getValue("showMoveIndicator").toInt();
1132 AppSettings->endGroup();
1133 m_theme.configure();
1134 m_theme.setEnabled(isEnabled());
1135 removeGuess();
1136 unselectSquare();
1137 if(size().height() >= minimumSize().height())
1138 {
1139 resizeBoard(size());
1140 }
1141 update();
1142 }
1143
selectSquare(Square s)1144 void BoardView::selectSquare(Square s)
1145 {
1146 // You can't select a square when guess move is enabled
1147 if(m_guessMove && !(m_flags & SuppressGuessMove))
1148 {
1149 return;
1150 }
1151 if(m_selectedSquare == s)
1152 {
1153 return;
1154 }
1155 unselectSquare();
1156 m_selectedSquare = s;
1157 update(squareRect(s));
1158 }
1159
unselectSquare()1160 void BoardView::unselectSquare()
1161 {
1162 Square prev = m_selectedSquare;
1163 m_selectedSquare = InvalidSquare;
1164 if(prev != InvalidSquare)
1165 {
1166 update(squareRect(prev));
1167 }
1168 }
1169
posFromSquare(int square) const1170 QPoint BoardView::posFromSquare(int square) const
1171 {
1172 int coord = m_coordinates ? CoordinateSize : 0;
1173 int x = isFlipped() ? 7 - square % 8 : square % 8;
1174 int y = isFlipped() ? square / 8 : 7 - square / 8;
1175 QPoint pos(coord + x * m_theme.size().width(), y * m_theme.size().height());
1176 return pos;
1177 }
1178
squareRect(Square square) const1179 QRect BoardView::squareRect(Square square) const
1180 {
1181 return QRect(posFromSquare(square), m_theme.size());
1182 }
1183
coordinateRectVertical(int n) const1184 QRect BoardView::coordinateRectVertical(int n) const
1185 {
1186 Q_ASSERT(m_coordinates);
1187 int x = isFlipped() ? n % 8 : 7 - n % 8;
1188 return QRect(QPoint(0, x * m_theme.size().height() + (m_theme.size().height() - CoordinateSize) / 2),
1189 QSize(CoordinateSize, CoordinateSize));
1190 }
1191
coordinateRectHorizontal(int n) const1192 QRect BoardView::coordinateRectHorizontal(int n) const
1193 {
1194 Q_ASSERT(m_coordinates);
1195 int y = isFlipped() ? 7 - n % 8 : n % 8;
1196 return QRect(QPoint(CoordinateSize + (y * (m_theme.size().width())) + (m_theme.size().width() - CoordinateSize) / 2,
1197 8 * m_theme.size().height()),
1198 QSize(CoordinateSize, CoordinateSize));
1199 }
1200
canDrop(Square s) const1201 bool BoardView::canDrop(Square s) const
1202 {
1203 if(m_dragged == Empty) // not dragging
1204 {
1205 return false;
1206 }
1207 if(s == InvalidSquare)
1208 {
1209 return false;
1210 }
1211 if(!(m_flags & IgnoreSideToMove))
1212 {
1213 return m_board.isMovable(s);
1214 }
1215 return true;
1216 }
1217
canDrag(Square s,Qt::KeyboardModifiers mdf) const1218 bool BoardView::canDrag(Square s, Qt::KeyboardModifiers mdf) const
1219 {
1220 if(m_dragged != Empty) // already dragging
1221 {
1222 return false;
1223 }
1224 if(s == InvalidSquare)
1225 {
1226 return false;
1227 }
1228 else if( (m_flags & IgnoreSideToMove) ||
1229 (moveActionFromModifier(mdf) == ActionAskEngine) ||
1230 (moveActionFromModifier(mdf) == ActionEvalMove))
1231 {
1232 return m_board.pieceAt(s) != Empty;
1233 }
1234 else
1235 {
1236 return m_board.isMovable(s);
1237 }
1238 }
1239
dragEnterEvent(QDragEnterEvent * event)1240 void BoardView::dragEnterEvent(QDragEnterEvent *event)
1241 {
1242 const BoardViewMimeData *mimeData = qobject_cast<const BoardViewMimeData *>(event->mimeData());
1243 if(mimeData)
1244 {
1245 event->acceptProposedAction();
1246 }
1247 }
1248
dragMoveEvent(QDragMoveEvent * event)1249 void BoardView::dragMoveEvent(QDragMoveEvent *event)
1250 {
1251 event->acceptProposedAction();
1252 }
1253
dragLeaveEvent(QDragLeaveEvent * event)1254 void BoardView::dragLeaveEvent(QDragLeaveEvent *event)
1255 {
1256 event->accept();
1257 }
1258
dropEvent(QDropEvent * event)1259 void BoardView::dropEvent(QDropEvent *event)
1260 {
1261 const BoardViewMimeData *mimeData = qobject_cast<const BoardViewMimeData *>(event->mimeData());
1262 if(mimeData)
1263 {
1264 Square s = squareAt(event->pos());
1265 if (s != InvalidSquare)
1266 {
1267 emit pieceDropped(s, mimeData->m_piece);
1268 }
1269 event->acceptProposedAction();
1270 }
1271 }
1272
drawSquareAnnotations(QPaintEvent * event)1273 void BoardView::drawSquareAnnotations(QPaintEvent* event)
1274 {
1275 QString annotation = m_board.squareAnnotation();
1276
1277 if(!annotation.isEmpty() && !annotation.isNull())
1278 {
1279 QStringList list = annotation.split(",");
1280
1281 for(QStringList::ConstIterator it = list.constBegin(); it != list.constEnd(); it++)
1282 {
1283 if(*it != "")
1284 {
1285 drawSquareAnnotation(event, *it);
1286 }
1287 }
1288 }
1289 }
1290
drawArrowAnnotations(QPaintEvent * event)1291 void BoardView::drawArrowAnnotations(QPaintEvent* event)
1292 {
1293 QString annotation = m_board.arrowAnnotation();
1294
1295 if(!annotation.isEmpty() && !annotation.isNull())
1296 {
1297 QStringList list = annotation.split(",");
1298
1299 for(QStringList::ConstIterator it = list.constBegin(); it != list.constEnd(); it++)
1300 {
1301 if(*it != "")
1302 {
1303 drawArrowAnnotation(event, *it);
1304 }
1305 }
1306 }
1307 }
1308
drawColorRect(QPaintEvent * event,Square square,QColor color,bool plain)1309 void BoardView::drawColorRect(QPaintEvent* event, Square square, QColor color, bool plain)
1310 {
1311 QPainter p(this);
1312
1313 QRect rect = squareRect(square);
1314 if(!event->region().intersects(rect))
1315 {
1316 return;
1317 }
1318
1319 int x = isFlipped() ? 7 - square % 8 : square % 8;
1320 int y = isFlipped() ? square / 8 : 7 - square / 8;
1321 QPoint pos(x * m_theme.size().width(), y * m_theme.size().height());
1322
1323 p.save();
1324 QPen pen(color);
1325 QBrush brush(color);
1326 p.setPen(pen);
1327 p.setBrush(brush);
1328 p.setOpacity(plain ? 0.7 : 0.35);
1329
1330 int coord = m_coordinates ? CoordinateSize : 0;
1331
1332 if (plain)
1333 {
1334 p.fillRect(coord + pos.x(), pos.y(), m_theme.size().width(), m_theme.size().height(), color);
1335 }
1336 else
1337 {
1338 QRadialGradient radialGrad(QPointF(coord + pos.x()+m_theme.size().width()/2, pos.y()+m_theme.size().height()/2), m_theme.size().width());
1339 radialGrad.setColorAt(0, color.lighter());
1340 radialGrad.setColorAt(0.5, color);
1341 radialGrad.setColorAt(1, color);
1342
1343 p.fillRect(coord + pos.x(), pos.y(), m_theme.size().width(), m_theme.size().height(), radialGrad);
1344 }
1345
1346 p.restore();
1347 }
1348
drawSquareAnnotation(QPaintEvent * event,QString annotation)1349 void BoardView::drawSquareAnnotation(QPaintEvent* event, QString annotation)
1350 {
1351 QString trimmed = annotation.simplified();
1352 QChar colorChar = trimmed[0];
1353 QChar fileChar = trimmed[1];
1354 QChar rankChar = trimmed[2];
1355 QString files = "abcdefgh";
1356 QString ranks = "12345678";
1357 unsigned char file = files.indexOf(fileChar);
1358 int rank = ranks.indexOf(rankChar);
1359
1360 QColor color = Qt::red;
1361 if(colorChar == 'Y')
1362 {
1363 color = Qt::yellow;
1364 }
1365 else if(colorChar == 'G')
1366 {
1367 color = Qt::green;
1368 }
1369 else if(colorChar == 'B')
1370 {
1371 color = Qt::blue;
1372 }
1373
1374 Square square = SquareFromRankAndFile(rank, file);
1375 drawColorRect(event, square, color);
1376 }
1377
drawArrow(int square1,int square2,QColor color,int thin)1378 void BoardView::drawArrow(int square1, int square2, QColor color, int thin)
1379 {
1380 QPainter p(this);
1381 thin = std::min(thin,3);
1382 if (thin) thin++;
1383
1384 float x1 = isFlipped() ? 7 - square1 % 8 : square1 % 8;
1385 float y1 = isFlipped() ? square1 / 8 : 7 - square1 / 8;
1386 float x2 = isFlipped() ? 7 - square2 % 8 : square2 % 8;
1387 float y2 = isFlipped() ? square2 / 8 : 7 - square2 / 8;
1388 float w = m_theme.size().width();
1389 float h = m_theme.size().height();
1390 float coord = m_coordinates ? CoordinateSize : 0;
1391 QPointF pos1(coord + (x1 * w) + (w / 2), (y1 * h) + (h / 2));
1392 QPointF pos2(coord + (x2 * w) + (w / 2), (y2 * h) + (h / 2));
1393
1394 // Now to Draw Arrow Head
1395 qreal headWidth = m_theme.size().width() / 4;
1396 qreal headLength = headWidth;
1397 qreal headIndent = headWidth / 4;
1398 qreal netIndent = headLength - headIndent;
1399
1400 qreal halfHead = headWidth / 2;
1401 float px1 = pos1.x();
1402 float px2 = pos2.x();
1403 float py1 = pos1.y();
1404 float py2 = pos2.y();
1405 float dX = px2 - px1;
1406 float dY = py2 - py1;
1407
1408 qreal arrowLength = qSqrt(dX * dX + dY * dY);
1409
1410 QPointF arrowPts[7];
1411
1412 // we will shorten the line somewhat to avoid arrows all colliding in the center of the square
1413 float adjust = ((w + h) / 8);
1414
1415 px1 = px1 + ((adjust * dX) / arrowLength);
1416 px2 = px2 - ((adjust * dX) / arrowLength);
1417 py1 = py1 + ((adjust * dY) / arrowLength);
1418 py2 = py2 - ((adjust * dY) / arrowLength);
1419
1420 // calculate the points that form the arrow
1421 arrowPts[0].setX(px2 - ((netIndent * dX) / arrowLength));
1422 arrowPts[0].setY(py2 - ((netIndent * dY) / arrowLength));
1423 arrowPts[4].setX(px2 - ((headLength * dX) / arrowLength));
1424 arrowPts[4].setY(py2 - ((headLength * dY) / arrowLength));
1425 arrowPts[1].setX(arrowPts[4].x() - ((halfHead * (dY)) / arrowLength));
1426 arrowPts[1].setY(arrowPts[4].y() - ((halfHead * (-dX)) / arrowLength));
1427 arrowPts[3].setX(arrowPts[4].x() + ((halfHead * (dY)) / arrowLength));
1428 arrowPts[3].setY(arrowPts[4].y() + ((halfHead * (-dX)) / arrowLength));
1429 arrowPts[2].setX(px2);
1430 arrowPts[2].setY(py2);
1431
1432 QPointF pos3(px1, py1);
1433 QPointF pos4(px2- ((adjust * dX) / arrowLength), py2- ((adjust * dY) / arrowLength));
1434
1435 p.save();
1436 p.setRenderHint(QPainter::SmoothPixmapTransform);
1437
1438 color.setAlpha(176-28*thin);
1439 int penWidth = std::max(2, (int)(headWidth / 3));
1440 QPen pen(color, penWidth, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
1441 p.setPen(pen);
1442 p.drawLine(pos3, pos4);
1443
1444 QBrush brush(color);
1445 p.setBrush(brush);
1446 pen.setWidth(2);
1447 p.setPen(pen);
1448 p.drawPolygon(arrowPts, 4);
1449
1450 p.restore();
1451 }
1452
drawArrowAnnotation(QPaintEvent * event,QString annotation)1453 void BoardView::drawArrowAnnotation(QPaintEvent* event, QString annotation)
1454 {
1455 static const QString letters = "abcdefgh";
1456 static const QString numbers = "12345678";
1457
1458 QString trimmed = annotation.simplified();
1459
1460 QChar colorChar = trimmed[0];
1461 QChar fileChar1 = trimmed[1];
1462 QChar rankChar1 = trimmed[2];
1463 QChar fileChar2 = trimmed[3];
1464 QChar rankChar2 = trimmed[4];
1465 unsigned char file1 = letters.indexOf(fileChar1);
1466 int rank1 = numbers.indexOf(rankChar1);
1467 unsigned char file2 = letters.indexOf(fileChar2);
1468 int rank2 = numbers.indexOf(rankChar2);
1469
1470 if(file1 > sizeof(letters) || file2 > sizeof(letters) || rank1 < 0 || rank2 < 0)
1471 {
1472 return;
1473 }
1474 Square square1 = SquareFromRankAndFile(rank1, file1);
1475 Square square2 = SquareFromRankAndFile(rank2, file2);
1476
1477 QRect rect1 = squareRect(square1);
1478 QRect rect2 = squareRect(square2);
1479 QRect u = rect1.united(rect2);
1480 if(!event->region().intersects(u))
1481 {
1482 return;
1483 }
1484
1485 QColor color = Qt::red;
1486 if(colorChar == 'Y')
1487 {
1488 color = Qt::yellow;
1489 }
1490 else if(colorChar == 'G')
1491 {
1492 color = Qt::green;
1493 }
1494 else if(colorChar == 'B')
1495 {
1496 color = Qt::blue;
1497 }
1498
1499 drawArrow(square1, square2, color);
1500 }
1501
dragged() const1502 Piece BoardView::dragged() const
1503 {
1504 return m_dragged;
1505 }
1506
setDragged(const Piece & dragged)1507 void BoardView::setDragged(const Piece &dragged)
1508 {
1509 m_dragged = dragged;
1510 }
1511
setEnabled(bool enabled)1512 void BoardView::setEnabled(bool enabled)
1513 {
1514 QWidget::setEnabled(enabled);
1515 }
1516
setDisabled(bool disabled)1517 void BoardView::setDisabled(bool disabled)
1518 {
1519 QWidget::setDisabled(disabled);
1520 }
1521
setDbIndex(QObject * dbIndex)1522 void BoardView::setDbIndex(QObject* dbIndex)
1523 {
1524 m_DbIndex = dbIndex;
1525 }
1526
dbIndex() const1527 QObject* BoardView::dbIndex() const
1528 {
1529 return m_DbIndex;
1530 }
1531
renderImage(QImage & image,double scaling) const1532 void BoardView::renderImage(QImage &image, double scaling) const
1533 {
1534 BoardView boardView(nullptr, BoardView::IgnoreSideToMove | BoardView::SuppressGuessMove);
1535 QSize s;
1536 if (scaling < 0)
1537 {
1538 if (AppSettings->getValue("/Board/fixedImageSize").toBool())
1539 {
1540 int n = AppSettings->getValue("/Board/copyImageSize").toInt();
1541 s = QSize(n, n*height()/width());
1542 }
1543 else
1544 {
1545 scaling = -scaling;
1546 s = size()*scaling;
1547 }
1548 }
1549 else
1550 {
1551 s = size()*scaling;
1552 }
1553
1554 boardView.setMinimumSize(s);
1555 boardView.setEnabled(isEnabled() && AppSettings->getValue("/Board/colorCopy").toBool());
1556 boardView.setFlipped(isFlipped());
1557 boardView.showMoveIndicator(showMoveIndicator());
1558 boardView.showCoordinates(showCoordinates());
1559 boardView.resize(s);
1560 boardView.configure();
1561 boardView.setBoard(board());
1562
1563 QPalette Pal(palette());
1564 Pal.setColor(QPalette::Background, Qt::transparent);
1565 boardView.setAutoFillBackground(true);
1566 boardView.setPalette(Pal);
1567 QRect sourceRect = boardView.totalRect();
1568 QPixmap pixmap(sourceRect.size());
1569 pixmap.fill(Qt::transparent);
1570
1571 boardView.render(&pixmap, QPoint(), sourceRect, QWidget::DrawChildren);
1572 image = pixmap.toImage();
1573 }
1574
renderImageForBoard(const BoardX & b,QSize size)1575 QImage BoardView::renderImageForBoard(const BoardX &b, QSize size)
1576 {
1577 QImage image;
1578 BoardView boardView(nullptr, BoardView::IgnoreSideToMove | BoardView::SuppressGuessMove);
1579 boardView.setBoard(b);
1580 boardView.setMinimumSize(size);
1581 boardView.resize(size);
1582 boardView.setEnabled(false);
1583 boardView.renderImage(image, 1.0);
1584 return image;
1585 }
1586