File indexing completed on 2024-11-24 03:43:19

0001 /*******************************************************************
0002 *
0003 * Copyright 2007  Aron Boström <c02ab@efd.lth.se>
0004 *
0005 * Bovo is free software; you can redistribute it and/or modify
0006 * it under the terms of the GNU General Public License as published by
0007 * the Free Software Foundation; either version 2, or (at your option)
0008 * any later version.
0009 *
0010 * Bovo is distributed in the hope that it will be useful,
0011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
0012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0013 * GNU General Public License for more details.
0014 *
0015 * You should have received a copy of the GNU General Public License
0016 * along with Bovo; see the file COPYING.  If not, write to
0017 * the Free Software Foundation, 51 Franklin Street, Fifth Floor,
0018 * Boston, MA 02110-1301, USA.
0019 *
0020 ********************************************************************/
0021 
0022 #include "scene.h"
0023 
0024 #include <QTime>
0025 #include <QTimer>
0026 #include <QPainter>
0027 #include <QGraphicsSceneMouseEvent>
0028 #include <QGraphicsView>
0029 #include <QSvgRenderer>
0030 
0031 #include <KConfig>
0032 #include <KConfigGroup>
0033 #include <KDesktopFile>
0034 
0035 #include "common.h"
0036 #include "coord.h"
0037 #include "game.h"
0038 #include "hintitem.h"
0039 #include "mark.h"
0040 #include "move.h"
0041 #include "theme.h"
0042 
0043 using namespace bovo;
0044 
0045 namespace gui {
0046 
0047 Scene::Scene(const Theme& theme, bool animation)
0048   : m_activate(false), m_game(nullptr), m_curCellSize(10.0), m_player(No), m_animation(animation),
0049   m_paintMarker(false), m_showLast(false) {
0050     /** @todo read theme from some configuration, I guess */
0051     /** @todo read file names from from some configuration, I guess */
0052     m_renderer = nullptr;
0053     loadTheme(theme);
0054     m_hintTimer = new QTimer(this);
0055     m_hintTimer->setSingleShot(true);
0056     m_hintItem = nullptr;
0057     setSceneRect( 0, 0, m_curCellSize*(NUMCOLS+2), m_curCellSize*(NUMCOLS+2) );
0058 }
0059 
0060 Scene::~Scene() {
0061     if (m_renderer) {
0062         m_renderer->deleteLater();
0063     }
0064     if (m_hintItem) {
0065         m_hintItem->deleteLater();
0066     }
0067     if (m_hintTimer) {
0068         m_hintTimer->disconnect();
0069         m_hintTimer->stop();
0070         m_hintTimer->deleteLater();
0071     }
0072 }
0073 
0074 qreal Scene::squareSize() const {
0075     return m_curCellSize;
0076 }
0077 
0078 void Scene::loadTheme(const Theme& theme) {
0079     m_fill = theme.fill();
0080     QColor color(theme.backgroundColor());
0081     QPalette bgPal;
0082     const auto vs = views();
0083     for (QGraphicsView* view : vs) {
0084         bgPal.setColor(view->backgroundRole(), color.isValid() ? color : Qt::white);
0085         view->setPalette(bgPal);
0086     }
0087     if (m_renderer == nullptr)
0088         m_renderer = new QSvgRenderer(theme.svg());
0089     else
0090         m_renderer->load(theme.svg());
0091     const QList<QGraphicsItem*> allMarks = items();
0092     for (QGraphicsItem* item : allMarks) {
0093         if (Mark* mark = qgraphicsitem_cast<Mark *>(item)) {
0094             mark->setFill(m_fill);
0095             mark->update(mark->boundingRect());
0096         }
0097         else if (auto hintItem = qgraphicsitem_cast<HintItem *>(item)) {
0098             hintItem->setFill(m_fill);
0099             hintItem->update(hintItem->boundingRect());
0100         }
0101     }
0102 }
0103 
0104 void Scene::setTheme(const Theme& theme) {
0105     loadTheme(theme);
0106     invalidate(0, 0, width(), height());
0107 }
0108 
0109 void Scene::activate(bool activate) {
0110     m_activate = activate;
0111 }
0112 
0113 void Scene::setWin() {
0114     if (!m_game->isGameOver()) {
0115         return;
0116     }
0117     invalidate(0, 0, width(), height());
0118 }
0119 
0120 void Scene::setGame(Game* game) {
0121     destroyHint();
0122     m_winningMoves = QList<Move>();
0123     m_game = game;
0124     m_player = m_game->player();
0125     connect(m_game, &Game::boardChanged, this, &Scene::updateBoard);
0126     if (!m_game->demoMode()) {
0127         connect(m_game, &Game::playerTurn, this, &Scene::slotPlayerTurn);
0128         connect(m_game, &Game::oposerTurn, this, &Scene::slotOposerTurn);
0129     }
0130     connect(m_game, &Game::gameOver, this, &Scene::slotGameOver);
0131     connect(this, &Scene::move, m_game, &Game::move);
0132 
0133     qDeleteAll(items()); //remove all marks
0134     m_paintMarker = false;
0135     m_showLast = false;
0136     invalidate(0, 0, width(), height());
0137 }
0138 
0139 void Scene::updateBoard(const Move& move) {
0140     destroyHint();
0141     if (move.valid()) {
0142         setShowLast(move.x(), move.y());
0143         Mark* mark = new Mark(this, move, m_animation, m_fill);
0144         mark->setSharedRenderer(m_renderer);
0145         addItem(mark);
0146     } else if (move.player() == No) {
0147         if (!m_game->isGameOver() && !m_winningMoves.empty()) {
0148             m_winningMoves = QList<Move>();
0149             invalidate(0, 0, width(), height());
0150         }
0151         removeShowLast();
0152         const QList<QGraphicsItem*> allMarks = items();
0153         for (QGraphicsItem* item : allMarks) {
0154             if (Mark* mark = qgraphicsitem_cast<Mark *>(item)) {
0155                 if (mark->row() == move.y() && mark->col() == move.x()) {
0156                     if (m_animation) {
0157                         connect(mark, &Mark::killed, this, &Scene::killMark);
0158                         mark->kill();
0159                     } else {
0160                         removeItem(mark);
0161                         delete mark;
0162                     }
0163                 }
0164             }
0165         }
0166     }
0167 }
0168 
0169 QPointF Scene::cellCenter(int x, int y) const {
0170     return cellTopLeft(x, y) + QPointF(m_curCellSize/2.0, m_curCellSize/2.0);
0171 }
0172 
0173 QPointF Scene::cellTopLeft(int x, int y) const {
0174     return {(x+1) * m_curCellSize, (y+1) * m_curCellSize};
0175 }
0176 
0177 void Scene::drawBackground(QPainter *p, const QRectF&) {
0178     QRectF sRect = sceneRect();
0179     int minSize = qMin(static_cast<int>(sRect.width()),
0180                        static_cast<int>(sRect.height()));
0181     qreal shrinkSize = static_cast<qreal>(NUMCOLS) /
0182                        static_cast<qreal>(NUMCOLS+2);
0183     qreal size = static_cast<qreal>(minSize) /
0184                  static_cast<qreal>(NUMCOLS+2);
0185 /*    sRect.setWidth(minSize*cellSize);
0186     sRect.setHeight(minSize*cellSize);
0187     sRect.setLeft(cellSize);*/
0188     QRectF tmpRect(size, size, minSize*shrinkSize, minSize*shrinkSize);
0189     m_renderer->render(p, QStringLiteral("background"));
0190     m_renderer->render(p, QStringLiteral("grid"), tmpRect);
0191 }
0192 
0193 void Scene::drawForeground(QPainter *p, const QRectF& bounds) {
0194     if (m_paintMarker) {
0195         QRectF rect(cellTopLeft(m_col, m_row), QSizeF(m_curCellSize,
0196                        m_curCellSize));
0197         qreal adjusting((1.0-m_fill)*m_curCellSize/2.0);
0198         rect.adjust(adjusting, adjusting, -adjusting, -adjusting);
0199         if (bounds.intersects(rect)) {
0200             p->setOpacity(0.4);
0201             m_renderer->render(p, QStringLiteral("x1"), rect);
0202             p->setOpacity(1);
0203         }
0204     }
0205     if (m_showLast) {
0206         QRectF rect(cellTopLeft(m_lastCol, m_lastRow), QSizeF(m_curCellSize,
0207                        m_curCellSize));
0208         if (bounds.intersects(rect)) {
0209             m_renderer->render(p, QStringLiteral("last"), rect);
0210         }
0211     }
0212     if (!m_winningMoves.empty()) {
0213         QList<Move>::const_iterator it = m_winningMoves.constBegin();
0214         QList<Move>::const_iterator end = m_winningMoves.constEnd();
0215         while (it != end) {
0216             QRectF tmpRect(cellTopLeft(it->x(), it->y()), QSizeF(m_curCellSize,
0217                            m_curCellSize));
0218             if (bounds.intersects(tmpRect)) {
0219                 m_renderer->render(p, QStringLiteral("win"), tmpRect);
0220             }
0221             ++it;
0222         }
0223     }
0224 }
0225 
0226 void Scene::mouseMoveEvent(QGraphicsSceneMouseEvent *ev) {
0227     if (!m_game->isGameOver() && !m_game->computerTurn() && m_activate) {
0228         QRectF boardRect(cellTopLeft(0, 0), QSizeF(m_curCellSize * NUMCOLS,
0229                          m_curCellSize * NUMCOLS));
0230         if (!boardRect.contains(ev->scenePos())) {
0231             removePaintMarker();
0232         } else {
0233             uint row = (uint)((ev->scenePos().y()-boardRect.y()) / m_curCellSize);
0234             uint col = (uint)((ev->scenePos().x()-boardRect.x()) / m_curCellSize);
0235             row = qMax(row, static_cast<uint>(0));
0236             row = qMin(static_cast<uint>(NUMCOLS-1), row);
0237             col = qMax(col, static_cast<uint>(0));
0238             col = qMin(static_cast<uint>(NUMCOLS-1), col);
0239             if (m_row != row || m_col != col || !m_paintMarker) {
0240                 if (m_game->player(Coord(col, row)) == No) {
0241                     setPaintMarker(col, row);
0242                 } else {
0243                     removePaintMarker();
0244                 }
0245             }
0246         }
0247     }
0248 }
0249 
0250 void Scene::mousePressEvent( QGraphicsSceneMouseEvent* ev ) {
0251     if (m_game->isGameOver() || m_game->computerTurn() || !m_activate) {
0252         return;
0253     }
0254     QRectF boardRect(cellTopLeft(0, 0), QSizeF(m_curCellSize * NUMCOLS,
0255                      m_curCellSize * NUMCOLS));
0256     if (!boardRect.contains(ev->scenePos())) {
0257         return;
0258     }
0259     int row = (int)((ev->scenePos().y()-boardRect.y()) / m_curCellSize);
0260     int col = (int)((ev->scenePos().x()-boardRect.x()) / m_curCellSize);
0261     row = qMax(row, 0);
0262     row = qMin(NUMCOLS-1, row);
0263     col = qMax(col, 0);
0264     col = qMin(NUMCOLS-1, col);
0265     Q_EMIT move(Move(m_player, Coord(col, row)));
0266 }
0267 
0268 void Scene::removePaintMarker() {
0269     if (!m_paintMarker) {
0270         return;
0271     }
0272     m_paintMarker = false;
0273     QRectF rectOld(cellTopLeft(m_col, m_row), QSizeF(m_curCellSize,
0274                    m_curCellSize));
0275     qreal adjusting((1.0-m_fill)*m_curCellSize/2.0);
0276     rectOld.adjust(adjusting, adjusting, -adjusting, -adjusting);
0277     invalidate(rectOld, ForegroundLayer);
0278 }
0279 
0280 void Scene::setPaintMarker(uint col, uint row) {
0281     if (m_paintMarker == true && m_col == col && m_row == row) {
0282         return;
0283     }
0284     removePaintMarker();
0285     m_paintMarker = true;
0286     m_col = col;
0287     m_row = row;
0288     QRectF rect(cellTopLeft(m_col, m_row), QSizeF(m_curCellSize,
0289                    m_curCellSize));
0290     qreal adjusting((1.0-m_fill)*m_curCellSize/2.0);
0291     rect.adjust(adjusting, adjusting, -adjusting, -adjusting);
0292     invalidate(rect, ForegroundLayer);
0293 }
0294 
0295 void Scene::removeShowLast() {
0296     if (!m_showLast) {
0297         return;
0298     }
0299     m_showLast = false;
0300     QRectF rectOld(cellTopLeft(m_lastCol, m_lastRow), QSizeF(m_curCellSize,
0301                    m_curCellSize));
0302     invalidate(rectOld, ForegroundLayer);
0303 }
0304 
0305 void Scene::setShowLast(uint col, uint row) {
0306     if (m_showLast == true && m_lastCol == col && m_lastRow == row) {
0307         return;
0308     }
0309     removeShowLast();
0310     m_showLast = true;
0311     m_lastCol = col;
0312     m_lastRow = row;
0313     QRectF rect(cellTopLeft(m_lastCol, m_lastRow), QSizeF(m_curCellSize,
0314                    m_curCellSize));
0315     invalidate(rect, ForegroundLayer);
0316 }
0317 
0318 void Scene::slotPlayerTurn() {
0319     activate(true);
0320 }
0321 
0322 void Scene::slotOposerTurn() {
0323     removePaintMarker();
0324     activate(false);
0325 }
0326 
0327 void Scene::slotGameOver(const QList<Move>& win) {
0328     removePaintMarker();
0329     removeShowLast();
0330     m_winningMoves = win;
0331     setWin();
0332     activate(false);
0333 }
0334 
0335 void Scene::hint(const Move& hint) {
0336     destroyHint();
0337     m_hintItem = new HintItem(this, hint, m_animation, m_fill);
0338     m_hintItem->setSharedRenderer(m_renderer);
0339     addItem(m_hintItem);
0340     connect(m_hintTimer, &QTimer::timeout, this, &Scene::hintTimeout);
0341     m_hintTimer->start(2000);
0342 }
0343 
0344 void Scene::destroyHint() {
0345     if (m_hintItem != nullptr) {
0346         m_hintTimer->disconnect();
0347         m_hintTimer->stop();
0348         m_hintItem->killAnimation();
0349         removeItem(m_hintItem);
0350         m_hintItem->deleteLater();
0351         m_hintItem = nullptr;
0352     }
0353 }
0354 
0355 void Scene::hintTimeout() {
0356     if (!m_animation) {
0357         destroyHint();
0358     } else if (m_hintItem != nullptr) {
0359         connect(m_hintItem, &HintItem::killed, this, &Scene::destroyHint);
0360         m_hintItem->kill();
0361     }
0362 }
0363 
0364 void Scene::enableAnimation(bool enabled) {
0365     m_animation = enabled;
0366     if (!m_animation) {
0367         killAnimations();
0368     }
0369 }
0370 
0371 void Scene::killAnimations() {
0372     if (m_hintItem != nullptr) {
0373         m_hintItem->killAnimation();
0374     }
0375 }
0376 
0377 void Scene::killMark(Mark* mark) {
0378     removeItem(mark);
0379     mark->deleteLater();
0380 }
0381 
0382 void Scene::replay() {
0383     const QList<QGraphicsItem*> allMarks = items();
0384     for (QGraphicsItem* mark : allMarks) {
0385         removeItem(mark);
0386         delete mark;
0387     }
0388     m_winningMoves = QList<Move>();
0389     invalidate(0, 0, width(), height());
0390     connect(m_game, &Game::boardChanged, this, &Scene::updateBoard);
0391 }
0392 
0393 bool Scene::event(QEvent *event) {
0394     bool ret = QGraphicsScene::event(event);
0395     if (event->type() == QEvent::Leave) {
0396         removePaintMarker();
0397     }
0398     return ret;
0399 }
0400 
0401 } /* namespace gui */
0402 
0403 #include "moc_scene.cpp"