Warning, file /games/ksudoku/src/gui/gamevariants.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /*************************************************************************** 0002 * Copyright 2007 Johannes Bergmeier <johannes.bergmeier@gmx.net> * 0003 * Copyright 2015 Ian Wadham <iandw.au@gmail.com> * 0004 * * 0005 * This program 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 of the License, or * 0008 * (at your option) any later version. * 0009 * * 0010 * This program 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 this program; if not, write to the * 0017 * Free Software Foundation, Inc., * 0018 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * 0019 ***************************************************************************/ 0020 0021 #include "gamevariants.h" 0022 #include "ksudoku_logging.h" 0023 #include "ksudokugame.h" 0024 #include "serializer.h" 0025 0026 #include <QEvent> 0027 #include <QIcon> 0028 #include <QPainter> 0029 0030 #include <KLocalizedString> 0031 #include <KMessageBox> 0032 0033 0034 0035 #include "puzzle.h" 0036 0037 0038 namespace ksudoku { 0039 0040 /////////////////////////////////////////////////////////////////////////////// 0041 // class GameVariant 0042 /////////////////////////////////////////////////////////////////////////////// 0043 0044 GameVariant::GameVariant(const QString& name, GameVariantCollection* collection) 0045 : m_name(name) 0046 { 0047 if(collection) 0048 collection->addVariant(this); 0049 } 0050 0051 void GameVariant::setDescription(const QString& descr) { 0052 m_description = descr; 0053 } 0054 0055 void GameVariant::setIcon(const QString& icon) { 0056 m_icon = icon; 0057 } 0058 0059 /////////////////////////////////////////////////////////////////////////////// 0060 // class GameVariantCollection 0061 /////////////////////////////////////////////////////////////////////////////// 0062 0063 GameVariantCollection::GameVariantCollection(QObject* parent, bool autoDel) 0064 : QAbstractListModel(parent), m_autoDelete(autoDel) 0065 { 0066 } 0067 0068 GameVariantCollection::~GameVariantCollection() { 0069 if(m_autoDelete) 0070 qDeleteAll(m_variants); 0071 m_variants.clear(); 0072 } 0073 0074 void GameVariantCollection::addVariant(GameVariant* variant) { 0075 int count = m_variants.count(); 0076 beginInsertRows(QModelIndex(), count, count); 0077 m_variants.append(variant); 0078 endInsertRows(); 0079 Q_EMIT newVariant(variant); 0080 } 0081 0082 int GameVariantCollection::rowCount(const QModelIndex& parent) const { 0083 Q_UNUSED(parent); 0084 return m_variants.count(); 0085 } 0086 0087 QModelIndex GameVariantCollection::index(int row, int column, const QModelIndex &parent) const { 0088 Q_UNUSED(parent); 0089 if ((row < 0) || (row >= m_variants.count())) 0090 return QModelIndex(); 0091 return createIndex(row, column, m_variants[row]); 0092 } 0093 0094 QVariant GameVariantCollection::data(const QModelIndex &index, int role) const { 0095 if (!index.isValid() || index.row() >= m_variants.count()) 0096 return QVariant(); 0097 0098 if (!index.internalPointer()) 0099 return QVariant(); 0100 0101 auto* gameVariant = static_cast<GameVariant*>(index.internalPointer()); 0102 0103 switch(role) { 0104 case Qt::DisplayRole: 0105 return gameVariant->name(); 0106 case Qt::DecorationRole: 0107 return gameVariant->icon(); 0108 case GameVariantDelegate::Description: 0109 return gameVariant->description(); 0110 } 0111 0112 return QVariant(); 0113 } 0114 0115 GameVariant* GameVariantCollection::variant(const QModelIndex& index) const { 0116 return static_cast<GameVariant*>(index.internalPointer()); 0117 } 0118 0119 /////////////////////////////////////////////////////////////////////////////// 0120 // class GameVariantDelegate 0121 ////////////77///////////////////////////////////////////////////////////////// 0122 0123 GameVariantDelegate::GameVariantDelegate(QObject* parent, QWidget * viewport) 0124 : QItemDelegate(parent), 0125 m_viewport(viewport) 0126 { 0127 } 0128 0129 QSize GameVariantDelegate::sizeHint(const QStyleOptionViewItem& option, 0130 const QModelIndex& index) const 0131 { 0132 Q_UNUSED(index); 0133 0134 // Fit a varying number of columns into the list-view. 0135 int viewportWidth = m_viewport->width(); 0136 int hintWidth = m_minWidth; 0137 int nItemsPerRow = viewportWidth / m_minWidth; 0138 0139 // Expand the hinted width, so that the columns will fill the viewport. 0140 if (nItemsPerRow > 0) { 0141 int adjustment = (viewportWidth % nItemsPerRow) ? 0 : 1; 0142 // The adjustment works around a graphics glitch, when nItemsPerRow 0143 // exactly divides viewportWidth and instead of nItemsPerRow columns 0144 // we suddenly get one column less, plus a large empty space, even 0145 // though QListView::spacing() is zero. 0146 hintWidth = (viewportWidth - adjustment) / nItemsPerRow; 0147 } 0148 0149 // Set the height to contain the icon or 4 lines of text. 0150 QSize size (hintWidth, 0); 0151 int fontHeight = option.fontMetrics.height(); 0152 if (m_iconHeight < fontHeight*4) { 0153 size.setHeight (fontHeight*4 + m_separatorPixels*3); 0154 } 0155 else { 0156 size.setHeight (m_iconHeight + m_separatorPixels*2); 0157 } 0158 return size; 0159 } 0160 0161 void GameVariantDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { 0162 0163 QColor bkgColor; 0164 0165 // Calculate item's column num in list-view. Add 1 in odd-numbered rows. 0166 int nItemsPerRow = qMax (m_viewport->width() / option.rect.width(), 1); 0167 int oddColumn = index.row() % nItemsPerRow; 0168 oddColumn = oddColumn + ((index.row() / nItemsPerRow) % 2); 0169 0170 if (option.state & QStyle::State_Selected) { 0171 bkgColor = option.palette.color(QPalette::Highlight); 0172 } else if (oddColumn % 2) { 0173 // The shading alternates along each row in the list view and 0174 // each odd-numbered row starts with a shaded item, so we have 0175 // a checkerboard pattern, regardless of whether nItemsPerRow 0176 // is odd or even (see the above calculation). 0177 bkgColor = option.palette.color(QPalette::AlternateBase); 0178 } else { 0179 bkgColor = option.palette.color(QPalette::Base); 0180 } 0181 painter->fillRect(option.rect, bkgColor); 0182 0183 QRect contentRect = option.rect.adjusted(m_leftMargin, m_separatorPixels, -m_rightMargin, -m_separatorPixels); 0184 0185 // Show icon 0186 0187 QPixmap iconPixmap = QIcon::fromTheme(icon(index)).pixmap(m_iconWidth, m_iconHeight); 0188 painter->drawPixmap(contentRect.left(), (contentRect.height() - iconPixmap.height()) / 2 + contentRect.top(), iconPixmap); 0189 contentRect.adjust(iconPixmap.width() + m_separatorPixels*2, 0, 0, 0); 0190 0191 // // Show configuration icon 0192 // if(configurable(index)) { 0193 // QPixmap configPixmap = QIcon::fromTheme( QLatin1String( "configure" ) ).pixmap(32, 32); 0194 // painter->drawPixmap(contentRect.right() - configPixmap.width(), (contentRect.height() - configPixmap.height()) / 2 + contentRect.top(), configPixmap); 0195 // contentRect.adjust(0, 0, -(configPixmap.width() + separatorPixels), 0); 0196 // } 0197 0198 // Show Title 0199 QFont titleFont(painter->font()); 0200 titleFont.setPointSize(titleFont.pointSize() + 2); 0201 titleFont.setWeight(QFont::Bold); 0202 0203 QPen previousPen(painter->pen()); 0204 // TODO: don't we have a function for 'color1 similar color2' 0205 if(previousPen.color() == bkgColor) { 0206 QPen p(previousPen); 0207 int color = bkgColor.rgb(); 0208 color = ~color | 0xff000000; 0209 p.setColor(color); 0210 painter->setPen(p); 0211 } 0212 0213 QFont previousFont(painter->font()); 0214 painter->setFont(titleFont); 0215 QString titleStr = painter->fontMetrics().elidedText(title(index), Qt::ElideRight, contentRect.width()); 0216 painter->drawText(contentRect, titleStr); 0217 contentRect.adjust(0, m_separatorPixels + painter->fontMetrics().height(), 0, 0); 0218 painter->setFont(previousFont); 0219 0220 // Show Description 0221 QTextOption wrap(Qt::AlignLeft); 0222 wrap.setWrapMode(QTextOption::WordWrap); 0223 QString descrStr = description(index); 0224 painter->drawText(contentRect, descrStr, wrap); 0225 0226 painter->setPen(previousPen); 0227 } 0228 0229 QString GameVariantDelegate::title(const QModelIndex& index) const { 0230 return index.model()->data(index, Qt::DisplayRole).toString(); 0231 } 0232 0233 QString GameVariantDelegate::description(const QModelIndex& index) const { 0234 return index.model()->data(index, Description).toString(); 0235 } 0236 0237 QString GameVariantDelegate::icon(const QModelIndex& index) const { 0238 return index.model()->data(index, Qt::DecorationRole).toString(); 0239 } 0240 0241 bool GameVariantDelegate::configurable(const QModelIndex& index) const { 0242 const auto* collection = dynamic_cast<const GameVariantCollection*>(index.model()); 0243 if(!collection) return false; 0244 0245 return collection->variant(index)->canConfigure(); 0246 } 0247 0248 bool GameVariantDelegate::eventFilter(QObject* watched, QEvent* event) { 0249 if(event->type() == QEvent::MouseButtonPress) { 0250 return true; 0251 } 0252 0253 // TODO insert code for handling clicks on buttons in items. 0254 0255 return QItemDelegate::eventFilter(watched, event); 0256 } 0257 0258 /////////////////////////////////////////////////////////////////////////////// 0259 // class SudokuGame 0260 /////////////////////////////////////////////////////////////////////////////// 0261 0262 SudokuGame::SudokuGame(const QString& name, uint order, GameVariantCollection* collection) 0263 : GameVariant(name, collection), m_order(order), m_graph(nullptr) 0264 { 0265 // TODO load from settings 0266 m_symmetry = 0; 0267 } 0268 0269 SudokuGame::~SudokuGame() 0270 { 0271 delete m_graph; 0272 } 0273 0274 bool SudokuGame::canConfigure() const { 0275 return true; 0276 } 0277 0278 bool SudokuGame::configure() { 0279 KMessageBox::information(nullptr, i18n("Configuration not yet implemented"), QLatin1String("")); 0280 return false; 0281 } 0282 0283 bool SudokuGame::canStartEmpty() const { 0284 return true; 0285 } 0286 0287 Game SudokuGame::startEmpty() { 0288 if(!m_graph) { 0289 m_graph = new SKGraph(m_order, TypeSudoku); 0290 m_graph->initSudoku(); 0291 } 0292 0293 auto* puzzle = new Puzzle(m_graph, false); 0294 puzzle->init(); 0295 0296 return Game(puzzle); 0297 } 0298 0299 Game SudokuGame::createGame(int difficulty, int symmetry) { 0300 if(!m_graph) { 0301 m_graph = new SKGraph(m_order, TypeSudoku); 0302 m_graph->initSudoku(); 0303 } 0304 0305 auto* puzzle = new Puzzle(m_graph, true); 0306 puzzle->init(difficulty, symmetry); 0307 0308 return Game(puzzle); 0309 } 0310 0311 KsView* SudokuGame::createView(const Game& /*game*/) const { 0312 qCDebug(KSudokuLog) << "KsView* ksudoku::SudokuGame::createView()"; 0313 return nullptr; 0314 } 0315 0316 /////////////////////////////////////////////////////////////////////////////// 0317 // class RoxdokuGame 0318 /////////////////////////////////////////////////////////////////////////////// 0319 0320 RoxdokuGame::RoxdokuGame(const QString& name, uint order, GameVariantCollection* collection) 0321 : GameVariant(name, collection), m_order(order), m_graph(nullptr) 0322 { 0323 m_symmetry = 0; 0324 } 0325 0326 RoxdokuGame::~RoxdokuGame() 0327 { 0328 delete m_graph; 0329 } 0330 0331 bool RoxdokuGame::canConfigure() const { 0332 return true; 0333 } 0334 0335 bool RoxdokuGame::configure() { 0336 KMessageBox::information(nullptr, i18n("Configuration not yet implemented"), QLatin1String("")); 0337 return false; 0338 } 0339 0340 bool RoxdokuGame::canStartEmpty() const { 0341 return true; 0342 } 0343 0344 Game RoxdokuGame::startEmpty() { 0345 if(!m_graph) { 0346 m_graph = new SKGraph(m_order, TypeRoxdoku); 0347 m_graph->initRoxdoku(); 0348 } 0349 0350 auto* puzzle = new Puzzle(m_graph, false); 0351 puzzle->init(); 0352 0353 return Game(puzzle); 0354 } 0355 0356 Game RoxdokuGame::createGame(int difficulty, int symmetry) { 0357 if(!m_graph) { 0358 m_graph = new SKGraph(m_order, TypeRoxdoku); 0359 m_graph->initRoxdoku(); 0360 } 0361 0362 auto* puzzle = new Puzzle(m_graph, true); 0363 puzzle->init(difficulty, symmetry); 0364 0365 return Game(puzzle); 0366 } 0367 0368 KsView* RoxdokuGame::createView(const Game& /*game*/) const { 0369 qCDebug(KSudokuLog) << "KsView* ksudoku::RoxdokuGame::createView()"; 0370 return nullptr; 0371 } 0372 0373 /////////////////////////////////////////////////////////////////////////////// 0374 // class CustomGame 0375 /////////////////////////////////////////////////////////////////////////////// 0376 0377 CustomGame::CustomGame(const QString& name, const QUrl& url, 0378 GameVariantCollection* collection) 0379 : GameVariant(name, collection), m_url(url), m_graph(nullptr) 0380 { 0381 m_symmetry = 0; 0382 } 0383 0384 CustomGame::~CustomGame() 0385 { 0386 delete m_graph; 0387 } 0388 0389 bool CustomGame::canConfigure() const { 0390 return false; 0391 } 0392 0393 bool CustomGame::configure() { 0394 return false; 0395 } 0396 0397 bool CustomGame::canStartEmpty() const { 0398 return true; 0399 } 0400 0401 Game CustomGame::startEmpty() { 0402 if (! createSKGraphObject()) { 0403 return Game(); 0404 } 0405 auto* puzzle = new Puzzle(m_graph, false); 0406 puzzle->init(); 0407 0408 return Game(puzzle); 0409 } 0410 0411 Game CustomGame::createGame(int difficulty, int symmetry) { 0412 if (! createSKGraphObject()) { 0413 return Game(); 0414 } 0415 auto* puzzle = new Puzzle(m_graph, true); 0416 puzzle->init(difficulty, symmetry); 0417 0418 return Game(puzzle); 0419 } 0420 0421 KsView* CustomGame::createView(const Game& /*game*/) const { 0422 qCDebug(KSudokuLog) << "KsView* ksudoku::CustomGame::createView()"; 0423 return nullptr; 0424 } 0425 0426 bool CustomGame::createSKGraphObject() 0427 { 0428 if ((m_graph != nullptr) && ((m_graph->specificType() == Mathdoku) || 0429 (m_graph->specificType() == KillerSudoku))) { 0430 delete m_graph; // Re-create SKGraph for every Mathdoku or 0431 m_graph = nullptr; // Killer Sudoku game (re-inits cages and size). 0432 } 0433 if (m_graph == nullptr) { 0434 QString errorMsg; 0435 m_graph = ksudoku::Serializer::loadCustomShape(m_url, nullptr, errorMsg); 0436 } 0437 return (m_graph != nullptr); // True if the shapes/*.xml file loaded OK. 0438 } 0439 0440 } 0441 0442 #include "moc_gamevariants.cpp"