summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIan Wadham <[email protected]>2015-07-12 10:26:52 +1000
committerIan Wadham <[email protected]>2015-07-12 12:08:19 +1000
commit3ec0b6cf79e2d4edf96368da47cdf3dcae331f86 (patch)
tree6eae8b7ef8316a709868b7da25853a3c340be7cb
parent1051d0a26436ce5fa39e0445b19dffb915206677 (diff)
Add GUI and engine for entering in Mathdoku and Killer Sudoku puzzles.
Added keystroke and mouse-click actions for entering cages in Mathdoku and Killer Sudoku puzzles. Provided corresponding update actions on the model and view of the puzzle being entered in. Added validation and error messages for all input.
-rw-r--r--src/gui/ksudokugame.cpp301
-rw-r--r--src/gui/ksudokugame.h35
-rw-r--r--src/gui/views/view2d.cpp119
-rw-r--r--src/gui/views/view2d.h26
4 files changed, 456 insertions, 25 deletions
diff --git a/src/gui/ksudokugame.cpp b/src/gui/ksudokugame.cpp
index 5c8a1e0..b83172c 100644
--- a/src/gui/ksudokugame.cpp
+++ b/src/gui/ksudokugame.cpp
@@ -2,6 +2,7 @@
* Copyright 2007 Francesco Rossi <[email protected]> *
* Copyright 2006-2007 Mick Kappenburg <[email protected]> *
* Copyright 2006-2007 Johannes Bergmeier <[email protected]> *
+ * Copyright 2015 Ian Wadham <[email protected]> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
@@ -25,12 +26,18 @@
#include "history.h"
+#include "globals.h"
+
+#include <KMessageBox>
+#include <KLocale>
+
#include <QList>
#include <QDebug> // IDW
#include <kurl.h>
+class QWidget;
namespace ksudoku {
@@ -62,6 +69,8 @@ public:
}
inline void emitCellChange(int index) { emit cellChange(index); }
inline void emitFullChange() { emit fullChange(); }
+ inline void emitCageChange(int cageNumP1, bool showLabel)
+ { emit cageChange(cageNumP1, showLabel); }
public:
PuzzleState state;
@@ -75,6 +84,12 @@ public:
KUrl url;
QList<HistoryEvent> history;
int historyPos;
+
+ QVector<int> m_cage;
+ int m_cageValue;
+ CageOperator m_cageOperator;
+ int m_currentCageSaved;
+ QWidget * m_messageParent;
};
void Game::Private::undo() {
@@ -148,6 +163,8 @@ Game::Game(Puzzle* puzzle)
m_private->accumTime = 0;
m_private->time.start();
+
+ m_private->m_currentCageSaved = false;
}
Game::Game(const Game& game)
@@ -257,6 +274,14 @@ bool Game::setMarker(int index, int val, bool state) {
void Game::setValue(int index, int val) {
if(!m_private) return;
+ // If entering in a puzzle, Mathdoku/KillerSudoku has its own procedure.
+ if (! m_private->puzzle->hasSolution()) {
+ if (addToCage (index, val)) {
+ return; // Value went in a Mathdoku/KillerSudoku puzzle.
+ }
+ }
+
+ // Solve all kinds of puzzles or enter in a Sudoku or Roxdoku puzzle.
if(val > m_private->puzzle->order()) return;
if(m_private->state.given(index)) return;
@@ -271,6 +296,270 @@ void Game::setValue(int index, int val) {
checkCompleted();
}
+bool Game::addToCage (int pos, int val)
+{
+ SKGraph * g = m_private->puzzle->graph();
+ SudokuType t = g->specificType();
+ if ((t != Mathdoku) && (t != KillerSudoku)) {
+ return false; // We are not keying in a cage.
+ }
+ qDebug() << "\nGame::addToCage: pos" << pos << "action" << val;
+ if (! m_private->m_currentCageSaved) { // Start a new cage.
+ m_private->m_cage.clear();
+ m_private->m_cageValue = 0;
+ m_private->m_cageOperator = NoOperator;
+ }
+ if ((val != 32) && (! validCell (pos, g))) {
+ return true; // Invalid pos and not deleting: go no further.
+ }
+ CageOperator cageOp = m_private->m_cageOperator;
+ if ((val >= 1) && (val <= 9)) {
+ // Append a non-zero digit to the cage-value.
+ m_private->m_cageValue = 10 * m_private->m_cageValue + val;
+ }
+ else {
+ switch (val) {
+ case 24: // Qt::Key_X = multiply.
+ qDebug() << " Set operator x";
+ cageOp = Multiply;
+ break;
+ case 26: // Qt::Key_0
+ if (m_private->m_cageValue > 0) {
+ // Append a zero to the cage-value.
+ qDebug() << " Append 0 to" << m_private->m_cageValue;
+ m_private->m_cageValue = 10 * m_private->m_cageValue;
+ }
+ break;
+ case 27: // Qt::Key_Slash.
+ qDebug() << " Set operator /";
+ cageOp = Divide;
+ break;
+ case 28: // Qt::Key_Minus.
+ qDebug() << " Set operator -";
+ cageOp = Subtract;
+ break;
+ case 29: // Qt::Key_Plus.
+ qDebug() << " Set operator +";
+ cageOp = Add;
+ break;
+ case 30: // Left click or Qt::Key_Space = drop through and
+ break; // add cell to cage.
+ case 31: // Qt::Key_Return = end cage.
+ finishCurrentCage (g);
+ return true;
+ break;
+ case 32: // Right click or Delete/Bkspace = delete a whole cage.
+ deleteCageAt (pos, g);
+ return true;
+ break;
+ default:
+ return false;
+ break;
+ }
+ }
+
+ // TODO - In Killer Sudoku, show the operator during data-entry.
+ if (t == KillerSudoku) {
+ if (cageOp != NoOperator) {
+ KMessageBox::information (messageParent(),
+ i18n("In Killer Sudoku, the operator is always + or none "
+ "and KSudoku automatically sets the correct choice."),
+ i18n("Killer Sudoku Cage"), QString("KillerCageInfo"));
+ }
+ // Set the operator to none or Add, depending on the cage-size.
+ cageOp = (m_private->m_cage.size() > 1) ? Add : NoOperator;
+ }
+
+ // Valid keystroke and position: store and display the current cage.
+ m_private->m_cageOperator = cageOp;
+ if (m_private->m_cage.indexOf (pos) < 0) {
+ m_private->m_cage.append (pos); // Add cell to current cage.
+ }
+
+ // Change the last cage in the data-model in the SKGraph object.
+ if (m_private->m_currentCageSaved) { // If new cage, skip dropping.
+ int cageNum = g->cageCount() - 1;
+ qDebug() << " DROPPING CAGE" << cageNum
+ << "m_currentCageSaved" << m_private->m_currentCageSaved
+ << "m_cage" << m_private->m_cage;
+ g->dropCage (cageNum);
+ }
+ // Add a new cage or replace the previous version of the new cage.
+ g->addCage (m_private->m_cage,
+ m_private->m_cageOperator, m_private->m_cageValue);
+ qDebug() << " ADDED CAGE" << (g->cageCount() - 1)
+ << "value" << m_private->m_cageValue
+ << "op" << m_private->m_cageOperator
+ << m_private->m_cage;
+ m_private->m_currentCageSaved = true;
+
+ // Re-draw the boundary and label of the cage just added to the graph.
+ // We always display the label while the cage is being keyed in.
+ m_private->emitCageChange (g->cageCount(), true);
+ return true;
+}
+
+bool Game::validCell (int pos, SKGraph * g)
+{
+ // No checks of selected cell needed if it is in the current cage.
+ if (m_private->m_cage.indexOf (pos) >= 0) {
+ return true;
+ }
+ // Selected cell must not be already in another cage.
+ for (int n = 0; n < g->cageCount(); n++) {
+ qDebug() << " IS CELL" << pos << "IN CAGE" << n
+ << "OF" << g->cageCount() << "?";
+ if (g->cage(n).indexOf (pos) >= 0) {
+ KMessageBox::information (messageParent(),
+ i18n("The cell you have selected has already been "
+ "used in a cage."),
+ i18n("Error in Cage"));
+ return false;
+ }
+ }
+ // Cell must adjoin the current cage or be the first cell in it.
+ int cageSize = m_private->m_cage.size();
+ if (cageSize > 0) {
+ int ix = g->cellPosX(pos);
+ int iy = g->cellPosY(pos);
+ int max = g->order();
+ bool adjoining = false;
+ for (int n = 0; n < cageSize; n++) {
+ int cell = m_private->m_cage.at(n);
+ int dx = g->cellPosX(cell) - ix;
+ int dy = g->cellPosY(cell) - iy;
+ if ((dy == 0) && (((ix > 0) && (dx == -1)) ||
+ ((ix < max) && (dx == 1)))) {
+ adjoining = true; // Adjoining to left or right.
+ break;
+ }
+ if ((dx == 0) && (((iy > 0) && (dy == -1)) ||
+ ((iy < max) && (dy == 1)))) {
+ adjoining = true; // Adjoining above or below.
+ break;
+ }
+ }
+ if (! adjoining) {
+ KMessageBox::information (messageParent(),
+ i18n("The cell you have selected is not next to "
+ "any cell in the cage you are creating."),
+ i18n("Error in Cage"));
+ return false;
+ }
+ }
+ return true;
+}
+
+void Game::finishCurrentCage (SKGraph * g)
+{
+ qDebug() << "END CAGE: value" << m_private->m_cageValue
+ << "op" << m_private->m_cageOperator
+ << m_private->m_cage << "\n";
+ // If Killer Sudoku and cage-size > 1, force operator to be +.
+ if ((g->specificType() == KillerSudoku) &&
+ (m_private->m_cage.size() > 1)) {
+ m_private->m_cageOperator = Add;
+ }
+ // Validate the contents of the cage.
+ if ((! m_private->m_currentCageSaved) ||
+ (m_private->m_cage.size() == 0)) {
+ KMessageBox::information (messageParent(),
+ i18n("The cage you wish to complete has no cells in it yet. "
+ "Please click on a cell or key in + - / x or a number."),
+ i18n("Error in Cage"));
+ return; // Invalid - cannot finalise the cage.
+ }
+ else if (m_private->m_cageValue == 0) {
+ KMessageBox::information (messageParent(),
+ i18n("The cage you wish to complete has no value yet. "
+ "Please key in a number with one or more digits."),
+ i18n("Error in Cage"));
+ return; // Invalid - cannot finalise the cage.
+ }
+ else if ((m_private->m_cage.size() > 1) &&
+ (m_private->m_cageOperator == NoOperator)) {
+ KMessageBox::information (messageParent(),
+ i18n("The cage you wish to complete has more than one cell, "
+ "but it has no operator yet. Please key in + - / or x."),
+ i18n("Error in Cage"));
+ return; // Invalid - cannot finalise the cage.
+ }
+ else if ((m_private->m_cage.size() == 1) &&
+ (m_private->m_cageValue > g->order())) {
+ KMessageBox::information (messageParent(),
+ i18n("The cage you wish to complete has one cell, but its "
+ "value is too large. A single-cell cage must have a value "
+ "from 1 to %1 in a puzzle of this size.", g->order()),
+ i18n("Error in Cage"));
+ return; // Invalid - cannot finalise the cage.
+ }
+
+ // Save and display the completed cage.
+ if (m_private->m_cage.size() == 1) { // Display digit.
+ doEvent(HistoryEvent(m_private->m_cage.first(),
+ CellInfo(CorrectValue, m_private->m_cageValue)));
+ m_private->emitCellChange(m_private->m_cage.first());
+ m_private->emitModified(true);
+ }
+ // IDW TODO - Unhighlight the cage that is being entered.
+ m_private->emitCageChange (g->cageCount(), // No label in size 1.
+ (m_private->m_cage.size() > 1));
+ // Start a new cage.
+ m_private->m_currentCageSaved = false;
+}
+
+void Game::deleteCageAt (int pos, SKGraph * g)
+{
+ int cageNumP1 = g->cageCount();
+ if (cageNumP1 > 0) {
+ // IDW TODO - Hover-hilite the cage that is to be deleted.
+ cageNumP1 = 0;
+ for (int n = 0; n < g->cageCount(); n++) {
+ if (g->cage(n).indexOf (pos) >= 0) {
+ cageNumP1 = n + 1; // This cage is to be deleted.
+ break;
+ }
+ }
+ // If the right-click was on a cage, delete it.
+ if (cageNumP1 > 0) {
+ if(KMessageBox::questionYesNo (messageParent(),
+ i18n("Do you wish to delete this cage?"),
+ i18n("Delete Cage"), KGuiItem(i18n("Delete")),
+ KStandardGuiItem::cancel(), QString("CageDelConfirm"))
+ == KMessageBox::No) {
+ return;
+ }
+ if (g->cage(cageNumP1-1).size() == 1) { // Erase digit.
+ // Delete the digit shown in a size-1 cage.
+ doEvent(HistoryEvent(pos, CellInfo(CorrectValue, 0)));
+ m_private->emitCellChange(pos);
+ m_private->emitModified(true);
+ }
+ // Erase the cage boundary and label.
+ m_private->emitCageChange (-cageNumP1, false);
+ // Remove the cage from the puzzle's graph.
+ qDebug() << " DROP CAGE" << (cageNumP1 - 1);
+ g->dropCage (cageNumP1 - 1);
+ if (m_private->m_cage.indexOf (pos) >= 0) {
+ // The current cage was dropped.
+ m_private->m_currentCageSaved = false;
+ }
+ }
+ else {
+ KMessageBox::information (messageParent(),
+ i18n("The cell you have selected is not in any cage, "
+ "so the Delete action will not delete anything."),
+ i18n("Delete Cage"), QString("CageDelMissed"));
+ }
+ }
+ else {
+ KMessageBox::information (messageParent(),
+ i18n("The Delete action finds that there are no cages "
+ "to delete."), i18n("Delete Cage"));
+ qDebug() << "NO CAGES TO DELETE.";
+ }
+}
+
void Game::checkCompleted() {
if(!m_private || !m_private->puzzle->hasSolution()) return;
@@ -470,6 +759,18 @@ void Game::setUserHadHelp(bool hadHelp) {
m_private->hadHelp = hadHelp;
}
+void Game::setMessageParent(QWidget * messageParent)
+{
+ if (m_private) {
+ m_private->m_messageParent = messageParent;
+ }
+}
+
+QWidget * Game::messageParent()
+{
+ return (m_private ? m_private->m_messageParent : 0);
+}
+
}
#include "ksudokugame.moc"
diff --git a/src/gui/ksudokugame.h b/src/gui/ksudokugame.h
index 99b4de4..0e5a638 100644
--- a/src/gui/ksudokugame.h
+++ b/src/gui/ksudokugame.h
@@ -2,6 +2,7 @@
* Copyright 2007 Francesco Rossi <[email protected]> *
* Copyright 2006-2007 Mick Kappenburg <[email protected]> *
* Copyright 2006-2007 Johannes Bergmeier <[email protected]> *
+ * Copyright 2015 Ian Wadham <[email protected]> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
@@ -28,6 +29,10 @@
class KUrl;
+class SKGraph;
+
+class QWidget;
+
namespace ksudoku {
class Puzzle ;
@@ -51,6 +56,7 @@ signals:
void completed(bool isCorrect, const QTime& required, bool withHelp);
void cellChange(int index);
void fullChange();
+ void cageChange(int cageNum, bool showLabel);
};
/**
@@ -145,7 +151,21 @@ public:
* Sets whether cell @p index is @p given (A given cell is not changeable by the player).
*/
void setGiven(int index, bool given);
-
+
+ /**
+ * Constructs a cage for a Mathdoku or Killer Sudoku puzzle which is
+ * being entered in. Proceeds in steps of one keystroke at a time.
+ *
+ * @param[in] index The position of a cell.
+ * @param[in] val A digit for the value, an operator for the cage or
+ * a code, e.g. add a cell to the cage or finish it.
+ *
+ * @return True if the value was handled by addToCage(), false
+ * if it should be processed by setValue(), as for a
+ * Sudoku or Roxdoku puzzle.
+ */
+ bool addToCage (int pos, int val);
+
/**
* Gets the all current values of the game
*/
@@ -225,13 +245,22 @@ public:
* Returns the hitory event at position @p i
*/
HistoryEvent historyEvent(int i) const;
-
-
+
+ /**
+ * Set the parent of message-box dialogs (used in data-entry of cages).
+ */
+ void setMessageParent (QWidget * messageParent);
+ QWidget * messageParent();
+
private:
/**
* When the game was finished this function emits a @c completed()
*/
void checkCompleted();
+
+ void finishCurrentCage (SKGraph * graph);
+ void deleteCageAt (int pos, SKGraph * graph);
+ bool validCell (int pos, SKGraph * graph);
private:
class Private;
diff --git a/src/gui/views/view2d.cpp b/src/gui/views/view2d.cpp
index 43690a9..1b8b7e5 100644
--- a/src/gui/views/view2d.cpp
+++ b/src/gui/views/view2d.cpp
@@ -1,5 +1,6 @@
/***************************************************************************
* Copyright 2008 Johannes Bergmeier <[email protected]> *
+ * Copyright 2015 Ian Wadham <[email protected] *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
@@ -23,9 +24,7 @@
#include <QGraphicsPixmapItem>
#include <QGraphicsSceneEvent>
-#include <QtDebug>
-// #include <QFile> // TODO only for debug
-// #include <QEvent> // TODO only for debug
+#include <QtDebug> // IDW test.
#include <kdebug.h>
@@ -361,6 +360,8 @@ void GroupGraphicsItem::resize(int gridSize, bool highlight) {
int size = gridSize*2;
Renderer* r = Renderer::instance();
+ highlight = (m_type == GroupCage); // IDW test.
+ highlight = true; // IDW test.
GroupTypes standard = m_type & GroupUnhighlightedMask;
GroupTypes highlighted = m_type | GroupHighlight;
@@ -458,6 +459,7 @@ void View2DScene::init(const Game& game) {
m_groups.resize(g->cliqueCount() + g->cageCount());
// IDW TODO - Draw Killer Sudoku cages inside cell borders?
// Anyway, show 3x3 and 2x2 blocks in Killer Sudoku somehow.
+ bool hasBothBlocksAndCages = (g->specificType() == KillerSudoku);
for(int i = 0; i < g->cliqueCount(); ++i) {
// Set the shape of each group.
QVector<int> idx = g->clique(i);
@@ -468,27 +470,12 @@ void View2DScene::init(const Game& game) {
m_groups[i] = new GroupGraphicsItem(pts);
m_groups[i]->setParentItem(m_groupLayer);
// Avoid ugly crossings of cages and blocks in Killer Sudoku.
- // Show borders of blocks only if there are no cages present.
- m_groups[i]->hideBlockBorder((g->cageCount() > 0));
+ // Hide borders of blocks if there can be cages in the puzzle.
+ m_groups[i]->hideBlockBorder (hasBothBlocksAndCages);
}
- int offset = g->cliqueCount();
for(int i = 0; i < g->cageCount(); ++i) {
- // Set the shape of each Mathdoku or KillerSudoku cage.
- QVector<int> idx = g->cage(i);
- QVector<QPoint> pts = QVector<QPoint>(idx.size());
- for(int j = 0; j < idx.size(); ++j) {
- pts[j] = QPoint(g->cellPosX(idx[j]), g->cellPosY(idx[j]));
- }
- m_groups[offset + i] = new GroupGraphicsItem(pts, true);
- m_groups[offset + i]->setParentItem(m_groupLayer);
- // Set the label of each cage (value and operator), but...
- if (g->cage(i).size() > 1) { // Not in single cells.
- QString str = QString::number(g->cageValue(i));
- if (g->specificType() == Mathdoku) { // Not in KillerSudoku.
- str = str + QString(" /-x+").mid(g->cageOperator(i), 1);
- }
- m_cells[g->cageTopLeft(i)]->setCageLabel(str);
- }
+ // Create a cage: draw the label for all cages except size 1.
+ initCageGroup (i, (g->cage(i).size() > 1));
}
m_cursor = new QGraphicsPixmapItem();
@@ -498,6 +485,7 @@ void View2DScene::init(const Game& game) {
connect(m_game.interface(), SIGNAL(cellChange(int)), this, SLOT(update(int)));
connect(m_game.interface(), SIGNAL(fullChange()), this, SLOT(update()));
+ connect(m_game.interface(), SIGNAL(cageChange(int,bool)), this, SLOT(updateCage(int,bool)));
connect(m_gameActions, SIGNAL(selectValue(int)), this, SLOT(selectValue(int)));
connect(m_gameActions, SIGNAL(enterValue(int)), this, SLOT(enterValue(int)));
connect(m_gameActions, SIGNAL(markValue(int)), this, SLOT(flipMarkValue(int)));
@@ -507,6 +495,32 @@ void View2DScene::init(const Game& game) {
update(-1);
}
+void View2DScene::initCageGroup (int cageNum, bool drawLabel) {
+ // Set the graphical shape and look of a Mathdoku or KillerSudoku cage.
+ SKGraph* g = m_game.puzzle()->graph();
+ int offset = g->cliqueCount();
+ QVector<int> idx = g->cage(cageNum);
+ QVector<QPoint> pts = QVector<QPoint>(idx.size());
+ for(int j = 0; j < idx.size(); ++j) {
+ pts[j] = QPoint(g->cellPosX(idx[j]), g->cellPosY(idx[j]));
+ }
+ m_groups[offset + cageNum] = new GroupGraphicsItem(pts, true);
+ m_groups[offset + cageNum]->setParentItem(m_groupLayer);
+
+ // Set the label of the cage (value and operator), but, in a single-cell
+ // cage, draw it only during data-entry of the first cell of a cage.
+ if (! drawLabel) {
+ m_cells[g->cageTopLeft(cageNum)]->setCageLabel(QString());
+ }
+ else if (drawLabel || (g->cage(cageNum).size() > 1)) {
+ QString str = QString::number(g->cageValue(cageNum));
+ if (g->specificType() == Mathdoku) { // No op shown in KillerSudoku.
+ str = str + QString(" /-x+").mid(g->cageOperator(cageNum), 1);
+ }
+ m_cells[g->cageTopLeft(cageNum)]->setCageLabel(str);
+ }
+}
+
void View2DScene::setSceneSize(const QSize& size) {
// Called from View2D::resizeEvent() and View2D::settingsChanged().
m_highlightsOn = Settings::showHighlights();
@@ -549,6 +563,20 @@ void View2DScene::hover(int cell) {
}
void View2DScene::press(int cell, bool rightButton) {
+ // IDW TODO - Can we save the type and entry mode just once somewhere?
+ if (! m_game.puzzle()->hasSolution()) {
+ // Keying in a puzzle. Is it a Mathdoku or Killer Sudoku type?
+ // If so, right click ==> delete cage: left click ==> add a cell.
+ SKGraph * g = m_game.puzzle()->graph();
+ SudokuType t = g->specificType();
+ if ((t == Mathdoku) || (t == KillerSudoku)) {
+ // If it is, delete or add a cell in the cage being entered.
+ if (m_game.addToCage (cell, rightButton ? 32 : 30)) {
+ return;
+ }
+ }
+ }
+ // Normal mouse actions for working on any kind of KSudoku puzzle.
if(rightButton) {
m_game.flipMarker(cell, m_selectedValue);
} else {
@@ -600,7 +628,54 @@ void View2DScene::update(int cell) {
m_cells[cell]->setValues(values);
} break;
}
+ }
}
+
+void View2DScene::updateCage (int cageNumP1, bool drawLabel) {
+ if (cageNumP1 == 0) {
+ qDebug() << "ERROR: View2DScene::updateCage: cageNumP1 == 0.";
+ return;
+ }
+ SKGraph* g = m_game.puzzle()->graph();
+ int offset = g->cliqueCount();
+ bool deleting = (cageNumP1 < 0);
+ int cageNum = deleting ? (-cageNumP1 - 1) : (cageNumP1 - 1);
+ qDebug() << "View2DScene::updateCage" << cageNum << "offset" << offset
+ << "group count" << m_groups.count()
+ << "cageNumP1" << cageNumP1;
+ if ((cageNum >= 0) && (m_groups.count() > (offset + cageNum))){
+ // Remove the cage-label from its scene-cell item.
+ m_cells[g->cageTopLeft(cageNum)]->setCageLabel(QString());
+ // Remove the cage-graphics from the scene.
+ removeItem (m_groups.at (offset + cageNum));
+ qDebug() << "DELETING GROUP" << (offset + cageNum);
+ delete m_groups.at (offset + cageNum);
+ m_groups[offset + cageNum] = 0; // Re-use or remove this later.
+ }
+ else {
+ // Start adding a cage-group to the scene.
+ m_groups.resize(g->cliqueCount() + g->cageCount());
+ }
+ if (!deleting && (cageNum >= 0)) {
+ // Create or re-create the cage that is being entered in.
+ qDebug() << "CREATING or RE-CREATING CAGE" << cageNum;
+ initCageGroup (cageNum, drawLabel);
+ // IDW TODO - Should NOT need hilite settings, NOT hilite row/col.
+ // IDW TODO - May need to doctor LOCAL CLASS GroupGraphicsItem for
+ // hilite, deletion and removal of cage-label to happen.
+ m_groups[offset + cageNum]->setHighlight (true);
+ }
+ else {
+ // Deleting a cage: finish removing graphics item from scene-data.
+ qDebug() << "REMOVE GROUP NUMBER" << (offset + cageNum)
+ << "number of groups" << m_groups.size()
+ << "cageNum" << cageNum;
+ m_groups.remove (offset + cageNum);
+ qDebug() << " DONE: number of groups" << m_groups.size();
+ }
+
+ // Invoke the method in View2DScene that triggers a re-draw.
+ setSceneSize (views().first()->size());
}
void View2DScene::selectValue(int value) {
diff --git a/src/gui/views/view2d.h b/src/gui/views/view2d.h
index 693b218..28689a5 100644
--- a/src/gui/views/view2d.h
+++ b/src/gui/views/view2d.h
@@ -1,5 +1,6 @@
/***************************************************************************
* Copyright 2008 Johannes Bergmeier <[email protected]> *
+ * Copyright 2015 Ian Wadham <[email protected]> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
@@ -42,6 +43,22 @@ public:
~View2DScene();
public:
void init(const Game& game);
+
+ /**
+ * Set up the graphics for drawing a Mathdoku or Killer Sudoku cage.
+ * The list of cages goes at the end of the list of rows, columns and
+ * blocks, in the vector QVector<GroupGraphicsItem*> m_groups. Each
+ * GroupGraphicsItem is a graphical structure having cells, an outline
+ * or boundary and highlighting. In addition, cages have a cage-label
+ * containing the cage's value and operator.
+ *
+ * @param cageNum The position of the cage (0 to n) in model-data
+ * in the puzzle's SKGraph. Its position in the
+ * scene and view list is (offset + cageNum), where
+ * "offset" is the number of rows, cols and blocks.
+ * @param drawLabel Whether the cage-label is to be drawn.
+ */
+ void initCageGroup (int cageNum, bool drawLabel = true);
void setSceneSize(const QSize& size);
@@ -57,6 +74,15 @@ public slots:
void flipMarkValue(int val, int cell=-1);
void moveCursor(int dx, int dy);
void update(int cell = -1);
+ /**
+ * Add, replace or delete graphics for a Mathdoku or Killer Sudoku cage.
+ *
+ * @param cageNumP1 The cage-index (in lists of cages) + 1.
+ * Value 1 to N: Add or replace the cage-graphics.
+ * Value -1 to -N: delete the cage-graphics.
+ * Value 0: invalid.
+ */
+ void updateCage (int cageNumP1, bool drawLabel);
signals:
void valueSelected(int val);
protected: