1 /***********************************************************************
2  *
3  * Copyright (C) 2008, 2010, 2012, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Graeme Gott <graeme@gottcode.org>
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  *
18  ***********************************************************************/
19 
20 #include "window.h"
21 
22 #include "board.h"
23 #include "locale_dialog.h"
24 #include "score_board.h"
25 
26 #include <QAction>
27 #include <QApplication>
28 #include <QCloseEvent>
29 #include <QGridLayout>
30 #include <QLabel>
31 #include <QMenu>
32 #include <QMenuBar>
33 #include <QMessageBox>
34 #include <QPixmap>
35 #include <QSettings>
36 
37 /*****************************************************************************/
38 
Window(QWidget * parent,Qt::WindowFlags wf)39 Window::Window(QWidget *parent, Qt::WindowFlags wf)
40 :	QMainWindow(parent, wf)
41 {
42 	QWidget* contents = new QWidget(this);
43 	setCentralWidget(contents);
44 
45 	// Create preview
46 	m_preview = new QLabel(contents);
47 	m_preview->setFixedSize(80, 100);
48 	m_preview->setAutoFillBackground(true);
49 	{
50 		QPalette palette = m_preview->palette();
51 		palette.setColor(m_preview->backgroundRole(), Qt::black);
52 		m_preview->setPalette(palette);
53 	}
54 
55 	// Create level display
56 	m_level = new QLabel("0", contents);
57 	m_level->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
58 	m_level->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);
59 	m_level->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
60 
61 	// Create lines display
62 	m_lines = new QLabel("0", contents);
63 	m_lines->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
64 	m_lines->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);
65 	m_lines->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
66 
67 	// Create score display
68 	m_score = new QLabel("0", contents);
69 	m_score->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
70 	m_score->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);
71 	m_score->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
72 
73 	// Create scoreboard
74 	m_score_board = new ScoreBoard(this);
75 
76 	// Create board
77 	m_board = new Board(contents);
78 	connect(m_board, &Board::pauseAvailable, this, &Window::pauseAvailable);
79 	connect(m_board, &Board::nextPieceAvailable, m_preview, &QLabel::setPixmap);
80 	connect(m_board, &Board::levelUpdated, m_level, static_cast<void (QLabel::*)(int)>(&QLabel::setNum));
81 	connect(m_board, &Board::linesRemovedUpdated, m_lines, static_cast<void (QLabel::*)(int)>(&QLabel::setNum));
82 	connect(m_board, &Board::scoreUpdated, this, &Window::scoreUpdated);
83 	connect(m_board, static_cast<void (Board::*)(int,int,int)>(&Board::gameOver), m_score_board, &ScoreBoard::addHighScore);
84 	connect(m_board, static_cast<void (Board::*)(int,int,int)>(&Board::gameOver), this, &Window::gameOver);
85 	connect(m_board, &Board::gameStarted, this, &Window::newGame);
86 
87 	// Create overlay message
88 	QLabel* message = new QLabel(tr("Click to start a new game."), contents);
89 	message->setAttribute(Qt::WA_TransparentForMouseEvents);
90 	message->setAlignment(Qt::AlignCenter);
91 	message->setStyleSheet(
92 		"QLabel {"
93 			"background-color: rgba(255, 255, 255, 200);"
94 			"color: black;"
95 			"margin: 0;"
96 			"padding: 0.5em;"
97 			"border-radius: 0.5em;"
98 		"}");
99 	message->setWordWrap(true);
100 	connect(m_board, &Board::showMessage, message, &QLabel::show);
101 	connect(m_board, &Board::showMessage, message, &QLabel::setText);
102 	connect(m_board, &Board::hideMessage, message, &QLabel::hide);
103 	connect(m_board, &Board::hideMessage, message, &QLabel::clear);
104 
105 	// Create menus
106 	QMenu* menu = menuBar()->addMenu(tr("&Game"));
107 	menu->addAction(tr("&New"), m_board, SLOT(newGame()), QKeySequence::New);
108 	m_pause_action = menu->addAction(tr("&Pause"), m_board, SLOT(pauseGame()), tr("P"));
109 	m_pause_action->setEnabled(false);
110 	m_resume_action = menu->addAction(tr("&Resume"), m_board, SLOT(resumeGame()), tr("P"));
111 	m_resume_action->setVisible(false);
112 	menu->addSeparator();
113 	menu->addAction(tr("&Scores"), m_score_board, SLOT(show()));
114 	menu->addSeparator();
115 	QAction* action = menu->addAction(tr("&Quit"), this, SLOT(close()), QKeySequence::Quit);
116 	action->setMenuRole(QAction::QuitRole);
117 
118 	menu = menuBar()->addMenu(tr("&Settings"));
119 	menu->addAction(tr("Application &Language..."), this, SLOT(setLocale()));
120 
121 	menu = menuBar()->addMenu(tr("&Help"));
122 	action = menu->addAction(tr("&About"), this, SLOT(about()));
123 	action->setMenuRole(QAction::AboutRole);
124 	action = menu->addAction(tr("About &Qt"), qApp, SLOT(aboutQt()));
125 	action->setMenuRole(QAction::AboutQtRole);
126 
127 	// Layout window
128 	QGridLayout* layout = new QGridLayout(contents);
129 	layout->setContentsMargins(12, 12, 12, 12);
130 	layout->setSpacing(0);
131 	layout->setColumnStretch(0, 1);
132 	layout->setColumnMinimumWidth(1, 12);
133 	layout->setRowStretch(11, 1);
134 	layout->setRowMinimumHeight(2, 24);
135 	layout->setRowMinimumHeight(5, 24);
136 	layout->setRowMinimumHeight(8, 24);
137 	layout->addWidget(m_board, 0, 0, 12, 1);
138 	layout->addWidget(message, 0, 0, 12, 1, Qt::AlignCenter);
139 	layout->addWidget(new QLabel(tr("Next Piece"), contents), 0, 2, 1, 1, Qt::AlignCenter);
140 	layout->addWidget(m_preview, 1, 2, Qt::AlignCenter);
141 	layout->addWidget(new QLabel(tr("Level"), contents), 3, 2, 1, 1, Qt::AlignCenter);
142 	layout->addWidget(m_level, 4, 2);
143 	layout->addWidget(new QLabel(tr("Removed Lines"), contents), 6, 2, 1, 1, Qt::AlignCenter);
144 	layout->addWidget(m_lines, 7, 2);
145 	layout->addWidget(new QLabel(tr("Score"), contents), 9, 2, 1, 1, Qt::AlignCenter);
146 	layout->addWidget(m_score, 10, 2);
147 
148 	// Restore window
149 	restoreGeometry(QSettings().value("Geometry").toByteArray());
150 }
151 
152 /*****************************************************************************/
153 
closeEvent(QCloseEvent * event)154 void Window::closeEvent(QCloseEvent* event)
155 {
156 	QSettings().setValue("Geometry", saveGeometry());
157 	if (!m_board->endGame()) {
158 		event->ignore();
159 	}
160 }
161 
162 /*****************************************************************************/
163 
pauseAvailable(bool available)164 void Window::pauseAvailable(bool available)
165 {
166 	static QPixmap preview;
167 
168 	if (available) {
169 		m_pause_action->setVisible(true);
170 		m_resume_action->setVisible(false);
171 		m_preview->setPixmap(preview);
172 	} else {
173 		m_resume_action->setVisible(true);
174 		m_pause_action->setVisible(false);
175 		preview = *m_preview->pixmap();
176 		m_preview->clear();
177 	}
178 }
179 
180 /*****************************************************************************/
181 
scoreUpdated(int score)182 void Window::scoreUpdated(int score)
183 {
184 	m_score->setText(QString("%L1").arg(score));
185 
186 	int position = m_score_board->highScorePosition(score);
187 	QPalette palette = m_score->palette();
188 	if (position == 0) {
189 		palette.setColor(m_preview->foregroundRole(), Qt::red);
190 	} else if (position < 10) {
191 		palette.setColor(m_preview->foregroundRole(), Qt::blue);
192 	}
193 	m_score->setPalette(palette);
194 }
195 
196 /*****************************************************************************/
197 
newGame()198 void Window::newGame()
199 {
200 	m_pause_action->setEnabled(true);
201 
202 	QPalette palette = m_score->palette();
203 	palette.setColor(m_preview->foregroundRole(), Qt::black);
204 	m_score->setPalette(palette);
205 }
206 
207 /*****************************************************************************/
208 
gameOver()209 void Window::gameOver()
210 {
211 	m_pause_action->setEnabled(false);
212 }
213 
214 /*****************************************************************************/
215 
about()216 void Window::about()
217 {
218 	QMessageBox::about(this, tr("About"), QString("<p align='center'><big><b>%1 %2</b></big><br/>%3<br/><small>%4<br/>%5</small></p>")
219 		.arg(tr("Gottet"), QApplication::applicationVersion(),
220 			tr("A tetris clone I made for my wife"),
221 			tr("Copyright &copy; 2008-%1 Graeme Gott.").arg("2020"),
222 			tr("Released under the <a href=%1>GPL 3</a> license").arg("\"http://www.gnu.org/licenses/gpl.html\""))
223 	);
224 }
225 
226 /*****************************************************************************/
227 
setLocale()228 void Window::setLocale()
229 {
230 	LocaleDialog dialog;
231 	dialog.exec();
232 }
233 
234 /*****************************************************************************/
235