Рис. 20.9. «Вы действительно хотите завершить работу?».
Теперь давайте рассмотрим класс TicTacToe:
01 class TicTacToe : public QWidget
02 {
03 Q_OBJECT
04 public:
05 TicTacToe(QWidget *parent = 0);
06 bool gameInProgress() const;
07 QString saveState() const;
08 QSize sizeHint() const;
09 protected:
10 void paintEvent(QPaintEvent *event);
11 void mousePressEvent(QMouseEvent *event);
12 private:
13 enum { Empty = '-', Cross = 'X', Nought = '0' };
14 void clearBoard();
15 void restoreState();
16 QString sessionFileName() const;
17 QRect cellRect(int row, int column) const;
18 int cellWidth() const { return width() / 3; }
19 int cellHeight() const { return height() / 3; }
20 bool threeInARow(int row1, int col1, int row3, int col3) const;
21 char board[3][3];
22 int turnNumber;
23 };
Класс TicTacToe наследует QWidget и переопределяет функции sizeHint(), paintEvent() и mousePressEvent(). Он также обеспечивает функции gameInProgress() и saveState(), которые мы использовали в нашем классе Application.
01 TicTacToe::TicTacToe(QWidget *parent, const char *name)
02 : QWidget(parent, name)
03 {
04 clearBoard();
05 if (qApp->isSessionRestored())
06 restoreState();
07 setWindowTitle(tr("Tic-Tac-Toe"));
08 }
В конструкторе мы стираем игровое поле и, если приложение было вызвано с опцией —session, вызываем закрытую функцию restoreState() для восстановления старого сеанса.
01 void TicTacToe::clearBoard()
02 {
03 for (int row= 0; row < 3; ++row) {
04 for (int column = 0; column < 3; ++column) {
05 board[row][column] = Empty;
06 }
07 }
08 turnNumber = 0;
09 }
В функции clearBoard() мы стираем все ячейки и устанавливаем turnNumber на значение 0.
01 QString TicTacToe::saveState() const
02 {
03 QFile file(sessionFileName());
04 if (file.open(QIODevice::WriteOnly)) {
05 QTextStream out(&file);
06 for (int row = 0; row < 3; ++row) {
07 for (int column = 0; column < 3; ++column) {
08 out << board[row][column];
09 }
10 }
11 }
12 return file.fileName();
13 }
В функции saveState() мы записываем состояние игрового поля на диск. Формат достаточно простой: «X» для крестиков, «0» для ноликов и «—» для пустых ячеек.
01 QString TicTacToe::sessionFileName() const
02 {
03 return QDir::homePath() + "/.tictactoe_"
04 + qApp->sessionId() + "_" + qApp->sessionKey();
05 }
Закрытая функция sessionFileName() возвращает имя файла для текущего идентификатора сеанса и ключа сеанса. Данная функция используется как в saveState(), так и в restoreState(). Имя файла определяется на основе идентификатора сеанса и ключа сеанса.
01 void TicTacToe::restoreState()
02 {
03 QFile file(sessionFileName());
04 if (file.open(QIODevice::ReadOnly)) {
05 QTextStream in(&file);
06 for (int row = 0; row < 3; ++row) {
07 for (int column = 0; column < 3; ++column) {
08 in >> board[row][column];
09 if (board[row][column] != Empty)
10 ++turnNumber;
11 }
12 }
13 }
14 update();
15 }
В функции restoreState() мы загружаем файл восстанавливаемого сеанса и заполняем игровое поле его информацией. Мы рассчитываем значение переменной turnNumber исходя из количества крестиков и ноликов на игровом поле.
В конструкторе TicTacToe мы вызывали restoreState(), если функция QApplication::isSessionRestored() возвращала true. В этом случае sessionId() и sessionKey() возвращают именно те значения, которые были при прошлом сохранении состояния приложения, а функция sessionFileName() возвращает имя файла того сеанса.
Тестирование и отладка программного кода по управлению сеансами могут быть достаточно утомительным делом, поскольку нам приходится все время входить и выходить из системы. Один из способов, позволяющий избежать этого, заключается в применении стандартной утилиты xsm, предусмотренной в системе X11. При первом вызове xsm на экран выводятся окно менеджера сеансов и окно консольного режима. Все приложения, запускаемые с данного окна консольного режима, будут использовать xsm в качестве своего менеджера сеансов, а не стандартный общесистемный менеджер сеансов. Мы можем затем использовать окно xsm для завершения, рестарта или сброса сеанса и проконтролировать правильность поведения приложения. Подробное описание того, как это делается, вы найдете в сети Интернет по адресу http://doc.trolltech.com/4.1/session.html.