2 This file is part of Cute Chess.
4 Cute Chess is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 Cute Chess is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with Cute Chess. If not, see <http://www.gnu.org/licenses/>.
20 #include <chessgame.h>
21 #include <chessplayer.h>
22 #include <timecontrol.h>
23 #include <engineconfiguration.h>
24 #include <enginefactory.h>
25 #include <engineprocess.h>
26 #include <humanplayer.h>
28 #include "mainwindow.h"
29 #include "chessboardview.h"
30 #include "chessboardmodel.h"
31 #include "movelistmodel.h"
32 #include "newgamedlg.h"
33 #include "chessclock.h"
34 #include "engineconfigurationmodel.h"
35 #include "enginemanagementdlg.h"
36 #include "plaintextlog.h"
37 #include "gamedatabasemodel.h"
39 MainWindow::MainWindow()
41 setWindowTitle("Cute Chess");
43 QHBoxLayout
* clockLayout
= new QHBoxLayout();
44 for (int i
= 0; i
< 2; i
++)
46 m_chessClock
[i
] = new ChessClock();
47 clockLayout
->addWidget(m_chessClock
[i
]);
50 m_boardModel
= new ChessboardModel(this);
51 m_chessboardView
= new ChessboardView(this);
52 m_chessboardView
->setModel(m_boardModel
);
53 m_moveListModel
= new MoveListModel(this);
54 connect(m_boardModel
, SIGNAL(moveMade(const QModelIndex
&, const QModelIndex
&)),
55 m_chessboardView
, SLOT(onMoveMade(const QModelIndex
&, const QModelIndex
&)));
57 QVBoxLayout
* mainLayout
= new QVBoxLayout();
58 mainLayout
->addLayout(clockLayout
);
59 mainLayout
->addWidget(m_chessboardView
);
61 // The content margins look stupid when used with dock widgets. Drop the
62 // margins except from the top so that the chess clocks have spacing
63 mainLayout
->setContentsMargins(0,
64 style()->pixelMetric(QStyle::PM_LayoutTopMargin
), 0, 0);
66 QWidget
* mainWidget
= new QWidget(this);
67 mainWidget
->setLayout(mainLayout
);
68 setCentralWidget(mainWidget
);
70 setStatusBar(new QStatusBar());
72 m_engineConfigurations
= new EngineConfigurationModel(this);
82 void MainWindow::createActions()
84 m_newGameAct
= new QAction(tr("&New game..."), this);
85 m_newGameAct
->setShortcut(QKeySequence("Ctrl+N"));
87 m_quitGameAct
= new QAction(tr("&Quit"), this);
88 m_quitGameAct
->setShortcut(QKeySequence(tr("Ctrl+Q")));
90 m_manageEnginesAct
= new QAction(tr("Manage..."), this);
92 connect(m_newGameAct
, SIGNAL(triggered(bool)), this, SLOT(newGame()));
93 connect(m_quitGameAct
, SIGNAL(triggered(bool)), this, SLOT(close()));
95 connect (m_manageEnginesAct
, SIGNAL(triggered(bool)), this,
96 SLOT(manageEngines()));
99 void MainWindow::createMenus()
101 m_gameMenu
= menuBar()->addMenu(tr("&Game"));
102 m_gameMenu
->addAction(m_newGameAct
);
103 m_gameMenu
->addSeparator();
104 m_gameMenu
->addAction(m_quitGameAct
);
106 m_viewMenu
= menuBar()->addMenu(tr("&View"));
108 m_enginesMenu
= menuBar()->addMenu(tr("En&gines"));
109 m_enginesMenu
->addAction(m_manageEnginesAct
);
111 m_helpMenu
= menuBar()->addMenu(tr("&Help"));
114 void MainWindow::createToolBars()
116 // Create tool bars here, use actions from createActions()
117 // See: createActions(), QToolBar documentation
120 void MainWindow::createDockWindows()
123 QDockWidget
* engineDebugDock
= new QDockWidget(tr("Engine Debug"), this);
124 m_engineDebugLog
= new PlainTextLog(engineDebugDock
);
125 connect(m_engineDebugLog
, SIGNAL(saveLogToFileRequest()), this,
126 SLOT(saveLogToFile()));
127 engineDebugDock
->setWidget(m_engineDebugLog
);
129 addDockWidget(Qt::BottomDockWidgetArea
, engineDebugDock
);
132 QDockWidget
* gameDatabaseDock
= new QDockWidget(tr("Game Database"), this);
133 QTreeView
* gameDatabaseView
= new QTreeView(gameDatabaseDock
);
134 gameDatabaseView
->setModel(new GameDatabaseModel(this));
135 gameDatabaseView
->setAlternatingRowColors(true);
136 gameDatabaseView
->setRootIsDecorated(false);
137 gameDatabaseView
->setUniformRowHeights(true);
138 gameDatabaseDock
->setWidget(gameDatabaseView
);
140 addDockWidget(Qt::BottomDockWidgetArea
, gameDatabaseDock
);
141 tabifyDockWidget(engineDebugDock
, gameDatabaseDock
);
144 QDockWidget
* moveListDock
= new QDockWidget(tr("Move List"), this);
145 QTreeView
* moveListView
= new QTreeView(moveListDock
);
146 moveListView
->setModel(m_moveListModel
);
147 moveListView
->setAlternatingRowColors(true);
148 moveListView
->setRootIsDecorated(false);
149 moveListDock
->setWidget(moveListView
);
151 connect(m_moveListModel
, SIGNAL(rowsInserted(const QModelIndex
&, int, int)),
152 moveListView
, SLOT(scrollToBottom()));
154 addDockWidget(Qt::RightDockWidgetArea
, moveListDock
);
156 // Add toggle view actions to the View menu
157 m_viewMenu
->addAction(moveListDock
->toggleViewAction());
158 m_viewMenu
->addAction(engineDebugDock
->toggleViewAction());
159 m_viewMenu
->addAction(gameDatabaseDock
->toggleViewAction());
162 void MainWindow::readSettings()
166 int size
= settings
.beginReadArray("engines");
167 for (int i
= 0; i
< size
; i
++)
169 settings
.setArrayIndex(i
);
170 EngineConfiguration config
;
171 config
.setName(settings
.value("name").toString());
172 config
.setCommand(settings
.value("command").toString());
173 config
.setWorkingDirectory(
174 settings
.value("working_directory").toString());
175 config
.setProtocol(ChessEngine::Protocol(
176 settings
.value("protocol").toInt()));
178 m_engineConfigurations
->addConfiguration(config
);
183 void MainWindow::newGame()
185 NewGameDialog
dlg(m_engineConfigurations
, this);
186 if (dlg
.exec() != QDialog::Accepted
)
189 ChessPlayer
* player
[2] = { 0, 0 };
190 ChessGame
* chessgame
= new ChessGame(Chess::Variant::Standard
, this);
191 connect(chessgame
, SIGNAL(humanEnabled(bool)),
192 m_chessboardView
, SLOT(setEnabled(bool)));
195 tc
.setTimePerTc(180000);
196 tc
.setMovesPerTc(40);
198 for (int i
= 0; i
< 2; i
++)
200 Chess::Side side
= (Chess::Side
)i
;
201 if (dlg
.playerType(side
) == NewGameDialog::CPU
)
203 EngineConfiguration config
= m_engineConfigurations
->configuration(dlg
.selectedEngine(side
));
205 EngineProcess
* process
= new EngineProcess(this);
207 if (config
.workingDirectory().isEmpty())
208 process
->setWorkingDirectory(QDir::tempPath());
210 process
->setWorkingDirectory(config
.workingDirectory());
212 process
->start(config
.command());
213 if (!process
->waitForStarted())
215 qDebug() << "Cannot start the engine process:" << config
.command();
218 ChessEngine
* engine
= EngineFactory::createEngine(config
.protocol(), process
, this);
220 engine
->setName(config
.name());
225 player
[i
] = new HumanPlayer(this);
226 connect(m_chessboardView
, SIGNAL(humanMove(const GenericMove
&)),
227 player
[i
], SLOT(onHumanMove(const GenericMove
&)));
230 player
[i
]->setTimeControl(tc
);
231 chessgame
->setPlayer(side
, player
[i
]);
233 connect(player
[i
], SIGNAL(startedThinking(int)),
234 m_chessClock
[i
], SLOT(start(int)));
235 connect(player
[i
], SIGNAL(moveMade(const Chess::Move
&)),
236 m_chessClock
[i
], SLOT(stop()));
237 connect(chessgame
, SIGNAL(gameEnded()),
238 m_chessClock
[i
], SLOT(stop()));
239 connect(player
[i
], SIGNAL(debugMessage(const QString
&)),
240 m_engineDebugLog
, SLOT(appendPlainText(const QString
&)));
242 m_moveListModel
->setGame(chessgame
);
244 m_boardModel
->setBoard(chessgame
->board());
248 void MainWindow::manageEngines()
250 QList
<EngineConfiguration
> oldConfigurations
=
251 m_engineConfigurations
->configurations();
253 EngineManagementDialog
dlg(m_engineConfigurations
, this);
255 if (dlg
.exec() == QDialog::Accepted
)
259 const QList
<EngineConfiguration
> engines
= m_engineConfigurations
->configurations();
261 settings
.beginWriteArray("engines");
262 for (int i
= 0; i
< engines
.size(); i
++)
264 settings
.setArrayIndex(i
);
265 settings
.setValue("name", engines
.at(i
).name());
266 settings
.setValue("command", engines
.at(i
).command());
267 settings
.setValue("working_directory", engines
.at(i
).workingDirectory());
268 settings
.setValue("protocol", engines
.at(i
).protocol());
272 // Make sure that the settings are flushed to disk now
277 // Release the engine configurations model and use
278 // the old configurations as base for the new model
279 delete m_engineConfigurations
;
280 m_engineConfigurations
= new EngineConfigurationModel(oldConfigurations
);
284 void MainWindow::saveLogToFile()
286 PlainTextLog
* log
= qobject_cast
<PlainTextLog
*>(QObject::sender());
289 const QString fileName
= QFileDialog::getSaveFileName(this, tr("Save Log"),
290 QString(), tr("Text Files (*.txt);;All Files (*.*)"));
292 if (fileName
.isEmpty())
295 QFile
file(fileName
);
296 if (!file
.open(QFile::WriteOnly
| QFile::Text
))
298 QFileInfo
fileInfo(file
);
301 msgBox
.setIcon(QMessageBox::Warning
);
302 msgBox
.setWindowTitle("Cute Chess");
304 switch (file
.error())
306 case QFile::OpenError
:
307 case QFile::PermissionsError
:
309 tr("The file \"%1\" could not be saved because "
310 "of insufficient privileges.")
311 .arg(fileInfo
.fileName()));
313 msgBox
.setInformativeText(
314 tr("Try selecting a location where you have "
315 "the permissions to create files."));
318 case QFile::TimeOutError
:
320 tr("The file \"%1\" could not be saved because "
321 "the operation timed out.")
322 .arg(fileInfo
.fileName()));
324 msgBox
.setInformativeText(
325 tr("Try saving the file to a local or another "
330 msgBox
.setText(tr("The file \"%1\" could not be saved.")
331 .arg(fileInfo
.fileName()));
333 msgBox
.setInformativeText(file
.errorString());
341 QTextStream
out(&file
);
342 out
<< log
->toPlainText();