1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtScxml module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:BSD$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** BSD License Usage
18** Alternatively, you may use this file under the terms of the BSD license
19** as follows:
20**
21** "Redistribution and use in source and binary forms, with or without
22** modification, are permitted provided that the following conditions are
23** met:
24** * Redistributions of source code must retain the above copyright
25** notice, this list of conditions and the following disclaimer.
26** * Redistributions in binary form must reproduce the above copyright
27** notice, this list of conditions and the following disclaimer in
28** the documentation and/or other materials provided with the
29** distribution.
30** * Neither the name of The Qt Company Ltd nor the names of its
31** contributors may be used to endorse or promote products derived
32** from this software without specific prior written permission.
33**
34**
35** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
36** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
37** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
38** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
39** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
42** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
43** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
44** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
45** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
46**
47** $QT_END_LICENSE$
48**
49****************************************************************************/
50
51#include "mainwindow.h"
52
53#include <QComboBox>
54#include <QDir>
55#include <QFile>
56#include <QGridLayout>
57#include <QLabel>
58#include <QScxmlStateMachine>
59#include <QStringListModel>
60#include <QTextStream>
61#include <QToolButton>
62
63static int Size = 9;
64
65QT_USE_NAMESPACE
66
67static QVariantList emptyRow()
68{
69 QVariantList row;
70 for (int i = 0; i < Size; i++)
71 row.append(t: QVariant(0));
72 return row;
73}
74
75static QVariantMap readSudoku(const QString &fileName)
76{
77 QFile input(fileName);
78 input.open(flags: QIODevice::ReadOnly | QIODevice::Text);
79 QTextStream str(&input);
80 const QString data = str.readAll();
81
82 QVariantList initRowsVariant;
83 const QStringList rows = data.split(sep: QLatin1Char('\n'));
84 for (int i = 0; i < Size; i++) {
85 if (i < rows.count()) {
86 QVariantList initRowVariant;
87 const QStringList row = rows.at(i).split(sep: QLatin1Char(','));
88 for (int j = 0; j < Size; j++) {
89 const int val = j < row.count()
90 ? row.at(i: j).toInt() % (Size + 1) : 0;
91 initRowVariant.append(t: val);
92 }
93 initRowsVariant.append(t: QVariant(initRowVariant));
94 } else {
95 initRowsVariant.append(t: QVariant(emptyRow()));
96 }
97 }
98
99 QVariantMap dataVariant;
100 dataVariant.insert(QStringLiteral("initState"), avalue: initRowsVariant);
101
102 return dataVariant;
103}
104
105MainWindow::MainWindow(QScxmlStateMachine *machine, QWidget *parent) :
106 QWidget(parent),
107 m_machine(machine)
108{
109 const QVector<QToolButton *> initVector(Size, nullptr);
110 m_buttons = QVector<QVector<QToolButton *> >(Size, initVector);
111
112 QGridLayout *layout = new QGridLayout(this);
113
114 for (int i = 0; i < Size; i++) {
115 for (int j = 0; j < Size; j++) {
116 QToolButton *button = new QToolButton(this);
117 button->setSizePolicy(hor: QSizePolicy::Expanding,
118 ver: QSizePolicy::Expanding);
119 layout->addWidget(button, row: i + i / 3, column: j + j / 3);
120 m_buttons[i][j] = button;
121 connect(sender: button, signal: &QToolButton::clicked, slot: [this, i, j] () {
122 QVariantMap data;
123 data.insert(QStringLiteral("x"), avalue: i);
124 data.insert(QStringLiteral("y"), avalue: j);
125 m_machine->submitEvent(eventName: "tap", data);
126 });
127 }
128 }
129
130 for (int i = 0; i < 3; i++) {
131 for (int j = 0; j < 2; j++) {
132 QFrame *hFrame = new QFrame(this);
133 hFrame->setFrameShape(QFrame::HLine);
134 layout->addWidget(hFrame, row: 4 * j + 3, column: 4 * i, rowSpan: 1, columnSpan: 3);
135
136 QFrame *vFrame = new QFrame(this);
137 vFrame->setFrameShape(QFrame::VLine);
138 layout->addWidget(vFrame, row: 4 * i, column: 4 * j + 3, rowSpan: 3, columnSpan: 1);
139 }
140 }
141
142 m_startButton = new QToolButton(this);
143 m_startButton->setSizePolicy(hor: QSizePolicy::Expanding,
144 ver: QSizePolicy::Expanding);
145 m_startButton->setText(tr(s: "Start"));
146 layout->addWidget(m_startButton, row: Size + 3, column: 0, rowSpan: 1, columnSpan: 3);
147
148 connect(sender: m_startButton, signal: &QAbstractButton::clicked,
149 slot: [this] {
150 if (m_machine->isActive(scxmlStateName: "playing"))
151 m_machine->submitEvent(eventName: "stop");
152 else
153 m_machine->submitEvent(eventName: "start");
154 });
155
156 m_label = new QLabel(tr(s: "unsolved"));
157 m_label->setAlignment(Qt::AlignCenter);
158 layout->addWidget(m_label, row: Size + 3, column: 4, rowSpan: 1, columnSpan: 3);
159
160 m_undoButton = new QToolButton(this);
161 m_undoButton->setSizePolicy(hor: QSizePolicy::Expanding,
162 ver: QSizePolicy::Expanding);
163 m_undoButton->setText(tr(s: "Undo"));
164 m_undoButton->setEnabled(false);
165 layout->addWidget(m_undoButton, row: Size + 3, column: 8, rowSpan: 1, columnSpan: 3);
166
167 connect(sender: m_undoButton, signal: &QAbstractButton::clicked,
168 slot: [this] {
169 m_machine->submitEvent(eventName: "undo");
170 });
171
172 m_chooser = new QComboBox(this);
173 layout->addWidget(m_chooser, row: Size + 4, column: 0, rowSpan: 1, columnSpan: 11);
174
175 QDir dataDir(QLatin1String(":/data"));
176 QFileInfoList sudokuFiles = dataDir.entryInfoList(nameFilters: QStringList()
177 << "*.data");
178 for (const QFileInfo &sudokuFile : sudokuFiles) {
179 m_chooser->addItem(atext: sudokuFile.completeBaseName(),
180 auserData: sudokuFile.absoluteFilePath());
181 }
182
183 connect(sender: m_chooser, signal: QOverload<int>::of(ptr: &QComboBox::currentIndexChanged),
184 slot: [this] (int index) {
185 const QString sudokuFile = m_chooser->itemData(index).toString();
186 const QVariantMap initValues = readSudoku(fileName: sudokuFile);
187 m_machine->submitEvent(eventName: "setup", data: initValues);
188 });
189
190 const QVariantMap initValues = readSudoku(
191 fileName: m_chooser->itemData(index: 0).toString());
192 m_machine->setInitialValues(initValues);
193
194 m_machine->connectToState(scxmlStateName: "playing", functor: [this] (bool playing) {
195 if (playing) {
196 m_startButton->setText(tr(s: "Stop"));
197 m_undoButton->setEnabled(true);
198 m_chooser->setEnabled(false);
199 } else {
200 m_startButton->setText(tr(s: "Start"));
201 m_undoButton->setEnabled(false);
202 m_chooser->setEnabled(true);
203 }
204 });
205
206 m_machine->connectToState(scxmlStateName: "solved", functor: [this] (bool solved) {
207 if (solved)
208 m_label->setText(tr(s: "SOLVED !!!"));
209 else
210 m_label->setText(tr(s: "unsolved"));
211 });
212
213 m_machine->connectToEvent(scxmlEventSpec: "updateGUI", functor: [this] (const QScxmlEvent &event) {
214 const QVariant data = event.data();
215
216 const QVariantList currentRows = data.toMap().value(
217 akey: "currentState").toList();
218 for (int i = 0; i < currentRows.count(); i++) {
219 const QVariantList row = currentRows.at(i).toList();
220 for (int j = 0; j < row.count(); j++) {
221 const int value = row.at(i: j).toInt();
222 const QString text = value ? QString::number(value) : QString();
223 m_buttons[i][j]->setText(text);
224 }
225 }
226
227 const bool active = m_machine->isActive(scxmlStateName: "playing");
228
229 const QVariantList initRows = data.toMap().value(akey: "initState").toList();
230 for (int i = 0; i < initRows.count(); i++) {
231 const QVariantList row = initRows.at(i).toList();
232 for (int j = 0; j < row.count(); j++) {
233 const int value = row.at(i: j).toInt();
234 // enable only zeroes from initState
235 const bool enabled = !value && active;
236 m_buttons[i][j]->setEnabled(enabled);
237 }
238 }
239 });
240
241 setLayout(layout);
242}
243
244MainWindow::~MainWindow()
245{
246}
247
248

source code of qtscxml/examples/scxml/sudoku/mainwindow.cpp