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"