1 /*
2 SPDX-FileCopyrightText: 2008 Sascha Peilicke <sasch.pe@gmx.de>
3
4 SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
5 */
6
7 #include "setupwidget.h"
8 #include "game/game.h"
9 #include "preferences.h"
10 #include "gui/graphicsview/themerenderer.h"
11
12 #include <QFile>
13 #include <QIcon>
14 namespace Kigo {
15
SetupWidget(Game * game,QWidget * parent)16 SetupWidget::SetupWidget(Game *game, QWidget *parent)
17 : QWidget(parent)
18 , m_game(game)
19 , m_lastFileName()
20 , m_lastFixedHandicap(Preferences::fixedHandicapValue())
21 {
22 Q_ASSERT(m_game);
23 setupUi(this);
24
25 startButton->setIcon(QIcon::fromTheme( QStringLiteral( "media-playback-start" )));
26 const QPixmap whiteStone = ThemeRenderer::self()->renderElement(Kigo::ThemeRenderer::Element::WhiteStone, QSize(48, 48));
27 whiteStoneImageLabel->setPixmap(whiteStone);
28 const QPixmap blackStone = ThemeRenderer::self()->renderElement(Kigo::ThemeRenderer::Element::BlackStone, QSize(48, 48));
29 blackStoneImageLabel->setPixmap(blackStone);
30
31 connect(startButton, &QPushButton::clicked, this, &SetupWidget::startClicked);
32 connect(whiteIsComputerCheckBox, &QCheckBox::toggled, this, &SetupWidget::whiteIsComputer);
33 connect(blackIsComputerCheckBox, &QCheckBox::toggled, this, &SetupWidget::blackIsComputer);
34 connect(sizeSmall, &QRadioButton::clicked, this, &SetupWidget::onBoardSizeChanged);
35 connect(sizeMedium, &QRadioButton::clicked, this, &SetupWidget::onBoardSizeChanged);
36 connect(sizeBig, &QRadioButton::clicked, this, &SetupWidget::onBoardSizeChanged);
37 connect(sizeOther, &QRadioButton::clicked, this, &SetupWidget::onBoardSizeChanged);
38 connect(sizeOtherSpinBox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &SetupWidget::onBoardSizeChanged);
39 }
40
~SetupWidget()41 SetupWidget::~SetupWidget()
42 {
43 saveSettings();
44 }
45
newGame()46 void SetupWidget::newGame()
47 {
48 loadSettings();
49 m_game->init();
50 gameSetupStack->setCurrentIndex(0);
51 m_game->setBoardSize(Preferences::boardSize());
52 handicapSpinBox->setSuffix(ki18np(" Stone", " Stones"));
53 m_game->setFixedHandicap(Preferences::fixedHandicapValue());
54 }
55
loadedGame(const QString & fileName)56 void SetupWidget::loadedGame(const QString &fileName)
57 {
58 Q_ASSERT(!fileName.isEmpty());
59
60 loadSettings();
61 gameSetupStack->setCurrentIndex(1);
62 m_game->init(fileName);
63 m_lastFileName = fileName;
64
65 QFile file(fileName);
66 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
67 return;
68 }
69 QTextStream in(&file);
70 const QString content = in.readAll();
71 file.close();
72
73 QRegExp re;
74
75 // Parse additional game information from SGF file
76 re.setPattern(QStringLiteral("EV\\[([\\w ]+)\\]")); // Capture and set event
77 if (re.indexIn(content) > -1) {
78 eventLabel->setText(re.cap(1));
79 eventLabel->setVisible(true);
80 eventStaticLabel->setVisible(true);
81 } else {
82 eventLabel->setVisible(false);
83 eventStaticLabel->setVisible(false);
84 }
85
86 re.setPattern(QStringLiteral("PC\\[([\\w ,]+)\\]")); // location
87 if (re.indexIn(content) > -1) {
88 locationLabel->setText(re.cap(1));
89 locationLabel->setVisible(true);
90 locationStaticLabel->setVisible(true);
91 } else {
92 locationLabel->setVisible(false);
93 locationStaticLabel->setVisible(false);
94 }
95
96 re.setPattern(QStringLiteral("RO\\[(\\d+)\\]")); // Capture and set round
97 if (re.indexIn(content) > -1) {
98 roundLabel->setText(re.cap(1));
99 roundLabel->setVisible(true);
100 roundStaticLabel->setVisible(true);
101 } else {
102 roundLabel->setVisible(false);
103 roundStaticLabel->setVisible(false);
104 }
105 re.setPattern(QStringLiteral("DT\\[([\\w/\\-:\\.,]+)\\]")); // Capture and set date
106 if (re.indexIn(content) > -1) {
107 dateLabel->setText(re.cap(1));
108 dateLabel->setVisible(true);
109 dateStaticLabel->setVisible(true);
110 } else {
111 dateLabel->setVisible(false);
112 dateStaticLabel->setVisible(false);
113 }
114
115 re.setPattern(QStringLiteral("PB\\[([\\w ]+)\\]")); // Capture and set black player name
116 if (re.indexIn(content) > -1) {
117 blackPlayerName->setText(re.cap(1));
118 }
119 re.setPattern(QStringLiteral("BR\\[([\\w ]+)\\]")); // Capture and set black player rank
120 if (re.indexIn(content) > -1) {
121 blackPlayerName->setText(blackPlayerName->text() + " (" + re.cap(1) + ')');
122 }
123 re.setPattern(QStringLiteral("BT\\[([\\w ]+)\\]")); // black team
124 if (re.indexIn(content) > -1) {
125 blackPlayerName->setText(blackPlayerName->text() + " [" + re.cap(1) + ']');
126 }
127
128 re.setPattern(QStringLiteral("PW\\[([\\w ]+)\\]")); // Capture and set white player name
129 if (re.indexIn(content) > -1) {
130 whitePlayerName->setText(re.cap(1));
131 }
132 re.setPattern(QStringLiteral("WR\\[([\\w ]+)\\]")); // Capture and set white player rank
133 if (re.indexIn(content) > -1) {
134 whitePlayerName->setText(whitePlayerName->text() + " (" + re.cap(1) + ')');
135 }
136 re.setPattern(QStringLiteral("WT\\[([\\w ]+)\\]")); // white team
137 if (re.indexIn(content) > -1) {
138 whitePlayerName->setText(whitePlayerName->text() + " [" + re.cap(1) + ']');
139 }
140
141 re.setPattern(QStringLiteral("KM\\[(\\d+\\.?\\d*)\\]")); // Capture and set komi
142 if (re.indexIn(content) > -1) {
143 komiLabel->setText(re.cap(1));
144 komiLabel->setVisible(true);
145 komiStaticLabel->setVisible(true);
146 } else {
147 komiLabel->setVisible(false);
148 komiStaticLabel->setVisible(false);
149 }
150
151 re.setPattern(QStringLiteral("TM\\[(\\d+)\\]")); // time limit in seconds
152 if (re.indexIn(content) > -1) {
153 const int seconds = re.cap(1).toInt();
154 const int hours = seconds/3600;
155 const int minutes = (seconds/60)%60;
156 const QString minuteString = i18ncp("Time limit of a game in minutes", "%1 minute", "%1 minutes", minutes);
157 if (hours) {
158 timeLabel->setText(i18ncp("Time limit of a game, hours, minutes", "%1 hour, %2", "%1 hours, %2", hours, minuteString));
159 } else {
160 timeLabel->setText(minuteString);
161 }
162 timeLabel->setVisible(true);
163 timeStaticLabel->setVisible(true);
164 } else {
165 timeLabel->setVisible(false);
166 timeStaticLabel->setVisible(false);
167 }
168
169 re.setPattern(QStringLiteral("RE\\[([WB]\\+[\\w\\.]+)\\]")); // Capture and set score
170 if (re.indexIn(content) > -1) {
171 scoreLabel->setText(re.cap(1));
172 scoreLabel->setVisible(true);
173 scoreStaticLabel->setVisible(true);
174 } else {
175 scoreLabel->setVisible(false);
176 scoreStaticLabel->setVisible(false);
177 }
178
179 const int count = m_game->moveCount();
180 startMoveSpinBox->setSuffix(i18n(" of %1", count));
181 startMoveSpinBox->setMaximum(count); // And set it as maximum and current
182 startMoveSpinBox->setValue(count); // move.
183 startMoveSpinBox->setFocus(Qt::OtherFocusReason);
184 connect(startMoveSpinBox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &SetupWidget::on_startMoveSpinBox_valueChanged);
185 }
186
commit()187 void SetupWidget::commit()
188 {
189 saveSettings();
190
191 if (Preferences::whitePlayerHuman()) {
192 m_game->whitePlayer().setType(Player::Type::Human);
193 } else {
194 m_game->whitePlayer().setType(Player::Type::Computer);
195 }
196 m_game->whitePlayer().setStrength(Preferences::whitePlayerStrength());
197 m_game->whitePlayer().setName(Preferences::whitePlayerName());
198
199 if (Preferences::blackPlayerHuman()) {
200 m_game->blackPlayer().setType(Player::Type::Human);
201 } else {
202 m_game->blackPlayer().setType(Player::Type::Computer);
203 }
204 m_game->blackPlayer().setStrength(Preferences::blackPlayerStrength());
205 m_game->blackPlayer().setName(Preferences::blackPlayerName());
206
207 // Set additional configuration based on game type
208 if (gameSetupStack->currentIndex() == 0) { // The user configured a new game
209 m_game->setKomi(Preferences::komi());
210 } else { // The user configured a loaded game
211 //Note: None currently
212 }
213 m_game->gameSetup();
214 }
215
on_startMoveSpinBox_valueChanged(int value)216 void SetupWidget::on_startMoveSpinBox_valueChanged(int value)
217 {
218 if (!m_lastFileName.isEmpty()) {
219 m_game->init(m_lastFileName, value);
220 }
221
222 if (m_game->currentPlayer().isWhite()) {
223 playerLabel->setText(i18n("White to play"));
224 } else if (m_game->currentPlayer().isBlack()) {
225 playerLabel->setText(i18n("Black to play"));
226 } else {
227 playerLabel->setText(QLatin1String(""));
228 }
229 }
230
onBoardSizeChanged()231 void SetupWidget::onBoardSizeChanged()
232 {
233 if (sizeOther->isChecked()) { // Custom size enabled
234 sizeOtherSpinBox->setEnabled(true);
235 m_game->setBoardSize(sizeOtherSpinBox->value());
236 } else {
237 sizeOtherSpinBox->setEnabled(false);
238 if (sizeSmall->isChecked()) {
239 m_game->setBoardSize(9);
240 } else if (sizeMedium->isChecked()) {
241 m_game->setBoardSize(13);
242 } else if (sizeBig->isChecked()) {
243 m_game->setBoardSize(19);
244 }
245 }
246 updateHandicapBox(); // Handicap depends on board size
247 }
248
on_handicapSpinBox_valueChanged(int value)249 void SetupWidget::on_handicapSpinBox_valueChanged(int value)
250 {
251 m_lastFixedHandicap = value; // Treat 1 handicap as 0
252 m_game->init(); // Setting fixed handicap works only
253 m_game->setFixedHandicap(value == 1 ? 0 : value); // on a blank game board
254 }
255
updateHandicapBox()256 void SetupWidget::updateHandicapBox()
257 {
258 const int maxFixedHandicap = m_game->fixedHandicapUpperBound();
259 handicapSpinBox->setEnabled(maxFixedHandicap > 0);
260 handicapSpinBox->setMaximum(maxFixedHandicap);
261 handicapSpinBox->setValue(m_lastFixedHandicap);
262 on_handicapSpinBox_valueChanged(handicapSpinBox->value());
263 }
264
loadSettings()265 void SetupWidget::loadSettings()
266 {
267 whitePlayerName->setText(Preferences::whitePlayerName());
268 whiteStrengthSlider->setValue(Preferences::whitePlayerStrength());
269 whiteIsComputerCheckBox->setChecked(!Preferences::whitePlayerHuman());
270
271 blackPlayerName->setText(Preferences::blackPlayerName());
272 blackStrengthSlider->setValue(Preferences::blackPlayerStrength());
273 blackIsComputerCheckBox->setChecked(!Preferences::blackPlayerHuman());
274
275 komiSpinBox->setValue(Preferences::komi());
276 handicapSpinBox->setValue(Preferences::fixedHandicapValue());
277
278 switch (Preferences::boardSize()) {
279 case 9:
280 sizeSmall->setChecked(true);
281 break;
282 case 13:
283 sizeMedium->setChecked(true);
284 break;
285 case 19:
286 sizeBig->setChecked(true);
287 break;
288 default:
289 sizeOther->setChecked(true);
290 sizeOtherSpinBox->setEnabled(true);
291 sizeOtherSpinBox->setValue(Preferences::boardSize());
292 break;
293 }
294 }
295
saveSettings()296 void SetupWidget::saveSettings()
297 {
298 if (gameSetupStack->currentIndex() == 0) { // The user configured a new game
299 Preferences::setWhitePlayerName(whitePlayerName->text());
300 Preferences::setBlackPlayerName(blackPlayerName->text());
301 } else {
302 //Note: Don't save player names for a loaded game because the names set by
303 // the user are overridden by those found in the SGF file.
304 }
305
306 Preferences::setWhitePlayerStrength(whiteStrengthSlider->value());
307 Preferences::setBlackPlayerStrength(blackStrengthSlider->value());
308 Preferences::setWhitePlayerHuman(!whiteIsComputerCheckBox->isChecked());
309 Preferences::setBlackPlayerHuman(!blackIsComputerCheckBox->isChecked());
310
311 Preferences::setKomi(komiSpinBox->value());
312 Preferences::setFixedHandicapValue(handicapSpinBox->value());
313
314 if (sizeSmall->isChecked()) {
315 Preferences::setBoardSize(9);
316 } else if (sizeMedium->isChecked()) {
317 Preferences::setBoardSize(13);
318 } else if (sizeBig->isChecked()) {
319 Preferences::setBoardSize(19);
320 } else if (sizeOther->isChecked()) {
321 Preferences::setBoardSize(sizeOtherSpinBox->value());
322 }
323 Preferences::self()->save();
324 }
325
blackIsComputer(bool computer)326 void SetupWidget::blackIsComputer(bool computer)
327 {
328 blackPlayerStack->setCurrentIndex(computer ? 1 : 0);
329 }
330
whiteIsComputer(bool computer)331 void SetupWidget::whiteIsComputer(bool computer)
332 {
333 whitePlayerStack->setCurrentIndex(computer ? 1 : 0);
334 }
335
336 } // End of namespace Kigo
337