1 /***********************************************************************
2  *
3  * Copyright (C) 2007, 2008, 2009, 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 "new_game_dialog.h"
25 #include "scores.h"
26 #include "settings.h"
27 
28 #include <QAction>
29 #include <QApplication>
30 #include <QDir>
31 #include <QIcon>
32 #include <QList>
33 #include <QMenu>
34 #include <QMenuBar>
35 #include <QMessageBox>
36 #include <QPair>
37 #include <QProcess>
38 #include <QSettings>
39 #include <QString>
40 #include <QTimer>
41 #include <QToolBar>
42 #include <QWheelEvent>
43 
44 // ============================================================================
45 
fetchIcon(const QString & name)46 static QIcon fetchIcon(const QString& name)
47 {
48 	QIcon icon(QString(":/oxygen/64x64/%1.png").arg(name));
49 	icon.addFile(QString(":/oxygen/48x48/%1.png").arg(name));
50 	icon.addFile(QString(":/oxygen/32x32/%1.png").arg(name));
51 	icon.addFile(QString(":/oxygen/22x22/%1.png").arg(name));
52 	icon.addFile(QString(":/oxygen/16x16/%1.png").arg(name));
53 	return QIcon::fromTheme(name, icon);
54 }
55 
56 // ============================================================================
57 
Window()58 Window::Window()
59 :	m_pause_action(0)
60 {
61 	// Create game object
62 	m_board = new Board(this);
63 	setCentralWidget(m_board);
64 	m_board->setFocus();
65 
66 	// Create scores window
67 	m_scores = new Scores(this);
68 	connect(m_board, &Board::finished, m_scores, &Scores::addScore);
69 
70 	// Create actions
71 	if (iconSize().width() == 26) {
72 		setIconSize(QSize(24, 24));
73 	}
74 	initActions();
75 	m_pause_action->setCheckable(true);
76 	connect(m_pause_action, &QAction::toggled, m_board, &Board::pauseGame);
77 	connect(m_board, &Board::pauseAvailable, m_pause_action, &QAction::setEnabled);
78 	connect(m_board, &Board::pauseChecked, m_pause_action, &QAction::setChecked);
79 	connect(m_board, &Board::hintAvailable, m_hint_action, &QAction::setEnabled);
80 
81 	// Setup window
82 	restoreGeometry(QSettings().value("Geometry").toByteArray());
83 
84 	// Create auto-save timer
85 	QTimer *timer = new QTimer(this);
86 	connect(timer, &QTimer::timeout, m_board, &Board::saveGame);
87 	timer->start(300000);
88 }
89 
90 // ============================================================================
91 
closeEvent(QCloseEvent * event)92 void Window::closeEvent(QCloseEvent* event)
93 {
94 	QSettings().setValue("Geometry", saveGeometry());
95 	m_board->saveGame();
96 	QMainWindow::closeEvent(event);
97 }
98 
99 // ============================================================================
100 
event(QEvent * event)101 bool Window::event(QEvent* event)
102 {
103 	if ((event->type() == QEvent::WindowBlocked || event->type() == QEvent::WindowDeactivate) && m_pause_action && m_pause_action->isEnabled()) {
104 		m_pause_action->setChecked(true);
105 	}
106 	return QMainWindow::event(event);
107 }
108 
109 // ============================================================================
110 
wheelEvent(QWheelEvent * event)111 void Window::wheelEvent(QWheelEvent* event)
112 {
113 	if (event->angleDelta().y() > 0) {
114 		m_board->zoomIn();
115 	} else {
116 		m_board->zoomOut();
117 	}
118 	QMainWindow::wheelEvent(event);
119 }
120 
121 // ============================================================================
122 
initActions()123 void Window::initActions()
124 {
125 	// Create menubar
126 #if defined(Q_OS_MAC)
127 	qApp->setAttribute(Qt::AA_DontShowIconsInMenus);
128 #endif
129 
130 	QMenu* game_menu = menuBar()->addMenu(tr("&Game"));
131 	QAction* new_action = game_menu->addAction(fetchIcon("document-new"), tr("&New"), this, SLOT(newGame()), QKeySequence::New);
132 	m_pause_action = game_menu->addAction(fetchIcon("media-playback-pause"), tr("&Pause"));
133 	m_pause_action->setShortcut(tr("P"));
134 	m_hint_action = game_menu->addAction(fetchIcon("games-hint"), tr("&Hint"), m_board, SLOT(hint()));
135 	game_menu->addSeparator();
136 	game_menu->addAction(fetchIcon("games-highscores"), tr("High &Scores"), m_scores, SLOT(exec()));
137 	game_menu->addSeparator();
138 	QAction* quit_action = game_menu->addAction(fetchIcon("application-exit"), tr("&Quit"), this, SLOT(close()), QKeySequence::Quit);
139 	quit_action->setMenuRole(QAction::QuitRole);
140 
141 	QMenu* view_menu = menuBar()->addMenu(tr("View"));
142 	QAction* zoom_in_action = view_menu->addAction(fetchIcon("zoom-in"), tr("Zoom &In"), m_board, SLOT(zoomIn()), tr("Ctrl++"));
143 	connect(m_board, &Board::zoomInAvailable, zoom_in_action, &QAction::setEnabled);
144 	QAction* zoom_out_action = view_menu->addAction(fetchIcon("zoom-out"), tr("Zoom &Out"), m_board, SLOT(zoomOut()), tr("Ctrl+-"));
145 	connect(m_board, &Board::zoomOutAvailable, zoom_out_action, &QAction::setEnabled);
146 
147 	QMenu* settings_menu = menuBar()->addMenu(tr("&Settings"));
148 	settings_menu->addAction(fetchIcon("preferences-desktop-locale"), tr("Application &Language..."), this, SLOT(setLocale()));
149 	settings_menu->addAction(fetchIcon("games-config-options"), tr("&Preferences..."), this, SLOT(showSettings()));
150 
151 	QMenu* help_menu = menuBar()->addMenu(tr("&Help"));
152 	QAction* about_action = help_menu->addAction(fetchIcon("help-about"), tr("&About"), this, SLOT(about()));
153 	about_action->setMenuRole(QAction::AboutRole);
154 	about_action = help_menu->addAction(QIcon(":/qt-project.org/qmessagebox/images/qtlogo-64.png"), tr("About &Qt"), qApp, SLOT(aboutQt()));
155 	about_action->setMenuRole(QAction::AboutQtRole);
156 
157 	// Create toolbar
158 	QToolBar* toolbar = new QToolBar(this);
159 	toolbar->setFloatable(false);
160 	toolbar->setMovable(false);
161 	toolbar->setToolButtonStyle(Qt::ToolButtonFollowStyle);
162 	toolbar->addAction(new_action);
163 	toolbar->addAction(m_pause_action);
164 	toolbar->addAction(m_hint_action);
165 	toolbar->addSeparator();
166 	toolbar->addAction(zoom_in_action);
167 	toolbar->addAction(zoom_out_action);
168 	addToolBar(toolbar);
169 	setContextMenuPolicy(Qt::NoContextMenu);
170 }
171 
172 // ============================================================================
173 
about()174 void Window::about()
175 {
176 	QMessageBox::about(this, tr("About"), QString("<p><center><big><b>%1 %2</b></big><br/>%3<br/><small>%4<br/>%5</small></center></p><p><center>%6<br/><small>%7</small></center></p>")
177 		.arg(tr("CuteMaze"), QCoreApplication::applicationVersion(),
178 			tr("A top-down maze game"),
179 			tr("Copyright &copy; 2007-%1 Graeme Gott").arg("2020"),
180 			tr("Released under the <a href=%1>GPL 3</a> license").arg("\"http://www.gnu.org/licenses/gpl.html\""),
181 			tr("Icons are from the <a href=%1>Oxygen</a> theme").arg("\"http://www.oxygen-icons.org/\""),
182 			tr("Used under the <a href=%1>LGPL 3</a> license").arg("\"http://www.gnu.org/licenses/lgpl.html\""))
183 	);
184 }
185 
186 // ============================================================================
187 
newGame()188 void Window::newGame()
189 {
190 	NewGameDialog dialog(this);
191 	if (dialog.exec() == QDialog::Accepted) {
192 		m_board->newGame();
193 	}
194 }
195 
196 // ============================================================================
197 
showSettings()198 void Window::showSettings()
199 {
200 	Settings settings(this);
201 	connect(&settings, &Settings::settingsChanged, m_board, &Board::loadSettings);
202 	settings.exec();
203 }
204 
205 // ============================================================================
206 
setLocale()207 void Window::setLocale()
208 {
209 	LocaleDialog dialog;
210 	dialog.exec();
211 }
212 
213 // ============================================================================
214