1 /*
2 SPDX-FileCopyrightText: 2007-2009 Fela Winkelmolen <fela.kde@gmail.com>
3 SPDX-FileCopyrightText: 2010 Brian Croom <brian.s.croom@gmail.com>
4
5 SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
8 #include "mainwindow.h"
9
10 // own
11 #include "gameengine.h"
12 #include "canvaswidget.h"
13 #include "ui_generalsettings.h"
14 #include "settings.h"
15 #include "globals.h"
16 // KDEGames
17 #include <KScoreDialog>
18 #include <KgThemeSelector>
19 #include <KStandardGameAction>
20 // KF
21 #include <KStandardAction>
22 #include <KToggleFullScreenAction>
23 #include <KActionCollection>
24 #include <KMessageBox>
25 #include <KConfigDialog>
26 #include <KConfig>
27 // Qt
28 #include <QPointer>
29 #include <QMenuBar>
30 #include <QIcon>
31 #include <QAction>
32 #include <QKeySequence>
33
34 class GeneralSettings : public QWidget
35 {
36 Q_OBJECT
37 public:
GeneralSettings(QWidget * parent)38 explicit GeneralSettings(QWidget *parent)
39 : QWidget(parent)
40 {
41 ui.setupUi(this);
42 }
~GeneralSettings()43 ~GeneralSettings() override {}
44 private:
45 Ui::GeneralSettings ui;
46 };
47
MainWindow(QWidget * parent)48 MainWindow::MainWindow(QWidget *parent)
49 : KXmlGuiWindow(parent),
50 canvasWidget(new CanvasWidget(this))
51 {
52 gameEngine = new GameEngine(this);
53
54 m_cheatsEnabled = !qEnvironmentVariableIsEmpty("KDE_DEBUG");
55
56 connect(canvasWidget, &CanvasWidget::focusLost, this, &MainWindow::pauseGame);
57
58 connect(canvasWidget, &CanvasWidget::levelComplete, gameEngine, &GameEngine::loadNextLevel);
59 connect(canvasWidget, &CanvasWidget::gameEnded, this, &MainWindow::handleEndedGame);
60 connect(canvasWidget, &CanvasWidget::mousePressed, this, &MainWindow::handleMousePressed);
61
62 connect(gameEngine, &GameEngine::loadingNewGame, canvasWidget, &CanvasWidget::newGame);
63 connect(gameEngine, &GameEngine::newLine, canvasWidget, &CanvasWidget::showLine);
64 connect(gameEngine, &GameEngine::newGift, canvasWidget, &CanvasWidget::putGift);
65 connect(gameEngine, &GameEngine::ready, canvasWidget, &CanvasWidget::startGame);
66
67 setCentralWidget(canvasWidget);
68
69 setupActions();
70 setFocusProxy(canvasWidget);
71
72 QSize defaultSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
73 setupGUI(defaultSize,
74 KXmlGuiWindow::Keys | KXmlGuiWindow::Save | KXmlGuiWindow::Create);
75
76 if (isFullScreen()) {
77 menuBar()->hide();
78 } else {
79 menuBar()->show();
80 }
81
82 // show here (instead of in main) else the mouse can't be grabbed
83 show();
84 gameEngine->start(QStringLiteral("default"));
85 }
86
~MainWindow()87 MainWindow::~MainWindow()
88 {
89 delete gameEngine;
90 delete canvasWidget;
91 }
92
setupActions()93 void MainWindow::setupActions()
94 {
95 KStandardGameAction::gameNew(this, &MainWindow::startNewGame, actionCollection());
96
97 KStandardGameAction::quit(this, &MainWindow::close, actionCollection());
98
99 KStandardGameAction::highscores(this, &MainWindow::showHighscores, actionCollection());
100
101 KStandardAction::preferences(this, &MainWindow::configureSettings, actionCollection());
102
103 QAction *fullScreenAction = KStandardAction::fullScreen(this,
104 &MainWindow::viewFullScreen, this, actionCollection());
105 // set the default primary shortcut as alternate shortcut
106 // and make F the default
107 QKeySequence fullScreenShortcut(i18nc("Key (shortcut) to toggle full screen", "F"));
108 fullScreenAction->setShortcut(fullScreenShortcut);
109 QAction *fireAction = new QAction(this);
110 fireAction->setText(i18n("Fire the ball"));
111 actionCollection()->setDefaultShortcut(fireAction, Qt::Key_Space);
112 fireAction->setIcon(QIcon::fromTheme(QStringLiteral("kbreakout")));
113 connect(fireAction, &QAction::triggered, this, &MainWindow::fire);
114 connect(fireAction, &QAction::changed, canvasWidget, &CanvasWidget::updateFireShortcut);
115 actionCollection()->addAction(QStringLiteral("fire"), fireAction);
116
117 if (m_cheatsEnabled) {
118 QAction *cheatSkipLevelAction = new QAction(this);
119 cheatSkipLevelAction->setText(i18n("Skip level"));
120 actionCollection()->setDefaultShortcut(cheatSkipLevelAction, Qt::Key_S);
121 cheatSkipLevelAction->setIcon(QIcon::fromTheme(QStringLiteral("kbreakout")));
122 connect(cheatSkipLevelAction, &QAction::triggered, this, &MainWindow::cheatSkipLevel);
123 actionCollection()->addAction(QStringLiteral("cheatSkipLevel"), cheatSkipLevelAction);
124
125 QAction *cheatAddLifeAction = new QAction(this);
126 cheatAddLifeAction->setText(i18n("Add life"));
127 actionCollection()->setDefaultShortcut(cheatAddLifeAction, Qt::Key_L);
128 cheatAddLifeAction->setIcon(QIcon::fromTheme(QStringLiteral("kbreakout")));
129 connect(cheatAddLifeAction, &QAction::triggered, this, &MainWindow::cheatAddLife);
130 actionCollection()->addAction(QStringLiteral("cheatAddLife"), cheatAddLifeAction);
131 }
132
133 pauseAction = KStandardGameAction::pause(this, &MainWindow::setGamePaused, actionCollection());
134 // set custom keys
135 QList<QKeySequence> keys;
136 keys.append(i18nc("Key (shortcut) to pause the game", "P"));
137 keys.append(Qt::Key_Escape);
138 // the following won't work (no more than 2 shortcuts allowed..)
139 // TODO: make the pause key work
140 //keys.append(Qt::Key_Pause);
141 pauseAction->setShortcuts(keys);
142 }
143
configureSettings()144 void MainWindow::configureSettings()
145 {
146 if (KConfigDialog::showDialog(QStringLiteral("settings"))) {
147 return;
148 }
149 // else it doesn't exist, thus create the dialog
150
151 KConfigDialog *dialog = new KConfigDialog(this, QStringLiteral("settings"),
152 Settings::self());
153 dialog->setModal(true);
154
155 dialog->addPage(new KgThemeSelector(canvasWidget->getProvider()),
156 i18n("Theme"), QStringLiteral("games-config-theme"));
157
158 // TODO: when will the page be destroyed?
159 dialog->addPage(new GeneralSettings(dialog),
160 i18nc("General settings", "General"),
161 QStringLiteral("games-config-options"));
162
163 dialog->show();
164 }
165
showHighscores()166 void MainWindow::showHighscores()
167 {
168 KScoreDialog ksdialog(KScoreDialog::Name | KScoreDialog::Level, this);
169 ksdialog.addField(KScoreDialog::Custom1, i18n(" Time (hh:mm)"), QStringLiteral("moves"));
170
171 ksdialog.exec();
172 }
173
startNewGame()174 void MainWindow::startNewGame()
175 {
176 int ret = KMessageBox::warningYesNo(
177 this,
178 i18n("Starting a new game will end the current one!"),
179 i18n("New Game"),
180 KGuiItem(i18n("Start a New Game")),
181 KStandardGuiItem::cancel());
182
183 if (ret == KMessageBox::Yes) {
184 pauseAction->setChecked(false);
185 gameEngine->start(QStringLiteral("default"));
186 }
187 }
188
pauseGame()189 void MainWindow::pauseGame()
190 {
191 if (!pauseAction->isChecked()) {
192 pauseAction->activate(QAction::Trigger);
193 }
194 }
195
setGamePaused(bool paused)196 void MainWindow::setGamePaused(bool paused)
197 {
198 canvasWidget->setGamePaused(paused);
199 }
200
handleEndedGame(int score,int level,int time)201 void MainWindow::handleEndedGame(int score, int level, int time)
202 {
203
204 QTime t = QTime(0, 0).addSecs(time);
205 // TODO: check int overflow and fix 24 hours "overflow"
206 QString timeString = t.toString(QStringLiteral("HH:mm"));
207
208 const int ALL_LEVELS = -1;
209
210 KScoreDialog::FieldInfo scoreInfo;
211 scoreInfo[KScoreDialog::Score].setNum(score);
212 scoreInfo[KScoreDialog::Custom1] = timeString;
213 if (level == ALL_LEVELS) {
214 scoreInfo[KScoreDialog::Level] = i18n("Game won!");
215 } else {
216 scoreInfo[KScoreDialog::Level].setNum(level);
217 }
218
219 QPointer<KScoreDialog> ksdialog =
220 new KScoreDialog(KScoreDialog::Name | KScoreDialog::Level, this);
221 ksdialog->addField(KScoreDialog::Custom1, i18n("Time (hh:mm)"), QStringLiteral("moves"));
222 ksdialog->addScore(scoreInfo);
223 ksdialog->exec();
224
225 if (ksdialog) {
226 gameEngine->start(QStringLiteral("default"));
227 delete ksdialog;
228 }
229 }
230
fire()231 void MainWindow::fire()
232 {
233 if (pauseAction->isChecked()) {
234 pauseAction->activate(QAction::Trigger);
235 } else {
236 canvasWidget->fire();
237 }
238 }
239
cheatSkipLevel()240 void MainWindow::cheatSkipLevel()
241 {
242 if (pauseAction->isChecked()) {
243 pauseAction->activate(QAction::Trigger);
244 } else {
245 canvasWidget->cheatSkipLevel();
246 }
247 }
248
cheatAddLife()249 void MainWindow::cheatAddLife()
250 {
251 if (pauseAction->isChecked()) {
252 pauseAction->activate(QAction::Trigger);
253 } else {
254 canvasWidget->cheatAddLife();
255 }
256 }
257
viewFullScreen(bool fullScreen)258 void MainWindow::viewFullScreen(bool fullScreen)
259 {
260 KToggleFullScreenAction::setFullScreen(this, fullScreen);
261 if (fullScreen) {
262 menuBar()->hide();
263 } else {
264 menuBar()->show();
265 }
266 }
267
handleMousePressed()268 void MainWindow::handleMousePressed()
269 {
270 if (pauseAction->isChecked()) {
271 pauseAction->activate(QAction::Trigger);
272 return;
273 }
274
275 if (Settings::fireOnClick()) {
276 canvasWidget->fire();
277 return;
278 }
279
280 // not fire on click
281
282 // check if dontAskFireOnClick is set
283 // we want to override it's default effect
284 // if it's set to _any_ value we want to keep the
285 // settings of fireOnClick at false
286 bool dontAsk = false; // true if dontAskFireOnClick was set
287 #if 0 //QT5
288 KConfig config(componentData(), "kbreakoutrc");
289 if (config.hasGroup("Notification Messages")) {
290 KConfigGroup group(&config, "Notification Messages");
291 if (group.hasKey("dontAskFireOnClick")) {
292 dontAsk = true;
293 }
294 }
295 #endif
296 if (dontAsk == false) {
297 // ask the user if he wants to fire on mouse click
298 int res = KMessageBox::questionYesNo(
299 this,
300 i18n("Do you want to fire the ball on mouse click?\n"
301 "Answering Yes will make the game steal the\n"
302 "mouse cursor, pause the game to get\n"
303 "the cursor back."),
304 i18n("Fire on click?"),
305 KStandardGuiItem::yes(),
306 KStandardGuiItem::no(),
307 QStringLiteral("dontAskFireOnClick") // doesntAskAgainName
308 );
309
310 if (res == KMessageBox::Yes) {
311 Settings::setFireOnClick(true);
312 Settings::self()->save();
313 }
314 }
315 }
316
317 #include "mainwindow.moc"
318