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