1 /*
2     Copyright (C) 2002-2005, Jason Katz-Brown <jasonkb@mit.edu>
3     Copyright 2010 Stefan Majewsky <majewsky@gmx.net>
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 2 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, write to the Free Software
17     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18 */
19 
20 #include "kolf.h"
21 #include "editor.h"
22 #include "landscape.h"
23 #include "newgame.h"
24 #include "objects.h"
25 #include "obstacles.h"
26 #include "scoreboard.h"
27 
28 #include <KActionCollection>
29 #include <KIO/CopyJob>
30 #include <KIO/Job>
31 #include <KJobWidgets>
32 #include <KLocalizedString>
33 #include <KMessageBox>
34 #include <KScoreDialog>
35 #include <KSelectAction>
36 #include <KSharedConfig>
37 #include <KStandardAction>
38 #include <KStandardGameAction>
39 #include <KStandardGuiItem>
40 #include <KToggleAction>
41 #include <QFileDialog>
42 #include <QGridLayout>
43 #include <QMimeDatabase>
44 #include <QStandardPaths>
45 #include <QStatusBar>
46 #include <QTemporaryFile>
47 #include <QTimer>
48 
KolfWindow()49 KolfWindow::KolfWindow()
50     : KXmlGuiWindow(nullptr)
51 {
52 	setObjectName( QStringLiteral("Kolf" ));
53 	competition = false;
54 	game = nullptr;
55 	editor = nullptr;
56 	spacer = nullptr;
57 	scoreboard = nullptr;
58 	isTutorial = false;
59 
60 	setupActions();
61 
62 	m_itemFactory.registerType<Kolf::Slope>(QStringLiteral("slope"), i18n("Slope"));
63 	m_itemFactory.registerType<Kolf::Puddle>(QStringLiteral("puddle"), i18n("Puddle"));
64 	m_itemFactory.registerType<Kolf::Wall>(QStringLiteral("wall"), i18n("Wall"));
65 	m_itemFactory.registerType<Kolf::Cup>(QStringLiteral("cup"), i18n("Cup"), true); //true == addOnNewHole
66 	m_itemFactory.registerType<Kolf::Sand>(QStringLiteral("sand"), i18n("Sand"));
67 	m_itemFactory.registerType<Kolf::Windmill>(QStringLiteral("windmill"), i18n("Windmill"));
68 	m_itemFactory.registerType<Kolf::BlackHole>(QStringLiteral("blackhole"), i18n("Black Hole"));
69 	m_itemFactory.registerType<Kolf::Floater>(QStringLiteral("floater"), i18n("Floater"));
70 	m_itemFactory.registerType<Kolf::Bridge>(QStringLiteral("bridge"), i18n("Bridge"));
71 	m_itemFactory.registerType<Kolf::Sign>(QStringLiteral("sign"), i18n("Sign"));
72 	m_itemFactory.registerType<Kolf::Bumper>(QStringLiteral("bumper"), i18n("Bumper"));
73 	//NOTE: The plugin mechanism has been removed because it is not used anyway.
74 
75 	filename = QString();
76 	dummy = new QWidget(this);
77 	setCentralWidget(dummy);
78 	layout = new QGridLayout(dummy);
79 
80 	resize(420, 480);
81 }
82 
~KolfWindow()83 KolfWindow::~KolfWindow()
84 {
85 }
86 
setupActions()87 void KolfWindow::setupActions()
88 {
89 	// Game
90 	newAction = KStandardGameAction::gameNew(this, SLOT(newGame()), actionCollection());
91 	endAction = KStandardGameAction::end(this, SLOT(closeGame()), actionCollection());
92 	KStandardGameAction::quit(this, SLOT(close()), actionCollection());
93 
94 	saveAction = actionCollection()->addAction(KStandardAction::Save, QStringLiteral("game_save"), this, SLOT(save()));
95 	saveAction->setText(i18n("Save &Course"));
96 	saveAsAction = actionCollection()->addAction(KStandardAction::SaveAs, QStringLiteral("game_save_as"), this, SLOT(saveAs()));
97 	saveAsAction->setText(i18n("Save &Course As..."));
98 
99 	saveGameAction = actionCollection()->addAction(QStringLiteral("savegame"));
100 	saveGameAction->setText(i18n("&Save Game"));
101 	connect(saveGameAction, &QAction::triggered, this, &KolfWindow::saveGame);
102 	saveGameAsAction = actionCollection()->addAction(QStringLiteral("savegameas"));
103 	saveGameAsAction->setText(i18n("&Save Game As..."));
104 	connect(saveGameAsAction, &QAction::triggered, this, &KolfWindow::saveGameAs);
105 
106 	loadGameAction = KStandardGameAction::load(this, SLOT(loadGame()), actionCollection());
107 	highScoreAction = KStandardGameAction::highscores(this, SLOT(showHighScores()), actionCollection());
108 
109 	// Hole
110 	editingAction = new KToggleAction(QIcon::fromTheme( QStringLiteral( "document-properties") ), i18n("&Edit"), this);
111 	actionCollection()->addAction(QStringLiteral("editing"), editingAction);
112 	connect(editingAction, &QAction::triggered, this, &KolfWindow::emptySlot);
113 	actionCollection()->setDefaultShortcut(editingAction, Qt::CTRL+Qt::Key_E);
114 	newHoleAction = actionCollection()->addAction(QStringLiteral("newhole"));
115 	newHoleAction->setIcon(QIcon::fromTheme( QStringLiteral( "document-new" )));
116 	newHoleAction->setText(i18n("&New"));
117 	connect(newHoleAction, &QAction::triggered, this, &KolfWindow::emptySlot);
118 	actionCollection()->setDefaultShortcut(newHoleAction, Qt::CTRL+Qt::SHIFT+Qt::Key_N);
119 	clearHoleAction = actionCollection()->addAction(QStringLiteral("clearhole"));
120 	clearHoleAction->setIcon(QIcon::fromTheme( QStringLiteral( "edit-clear-locationbar-ltr" )));
121 	clearHoleAction->setText(KStandardGuiItem::clear().text());
122 	connect(clearHoleAction, &QAction::triggered, this, &KolfWindow::emptySlot);
123 	actionCollection()->setDefaultShortcut(clearHoleAction, Qt::CTRL+Qt::Key_Delete);
124 	resetHoleAction = actionCollection()->addAction(QStringLiteral("resethole"));
125 	resetHoleAction->setText(i18n("&Reset"));
126 	connect(resetHoleAction, &QAction::triggered, this, &KolfWindow::emptySlot);
127 	actionCollection()->setDefaultShortcut(resetHoleAction, Qt::CTRL+Qt::Key_R);
128 	undoShotAction = KStandardAction::undo(this, &KolfWindow::emptySlot, this);
129 	actionCollection()->addAction(QStringLiteral("undoshot"), undoShotAction);
130 	undoShotAction->setText(i18n("&Undo Shot"));
131 	//replayShotAction = new QAction(i18n("&Replay Shot"), 0, this, SLOT(emptySlot()), actionCollection(), "replay");
132 
133 	// Go
134 	holeAction = new KSelectAction(i18n("Switch to Hole"), this);
135 	actionCollection()->addAction(QStringLiteral("switchhole"), holeAction);
136 	connect(holeAction, &QAction::triggered, this, &KolfWindow::emptySlot);
137 	nextAction = actionCollection()->addAction(QStringLiteral("nexthole"));
138 	nextAction->setIcon(QIcon::fromTheme( QStringLiteral( "go-next" )));
139 	nextAction->setText(i18n("&Next Hole"));
140 	connect(nextAction, &QAction::triggered, this, &KolfWindow::emptySlot);
141 	actionCollection()->setDefaultShortcuts(nextAction, KStandardShortcut::forward());
142 	prevAction = actionCollection()->addAction(QStringLiteral("prevhole"));
143 	prevAction->setIcon(QIcon::fromTheme( QStringLiteral( "go-previous" )));
144 	prevAction->setText(i18n("&Previous Hole"));
145 	connect(prevAction, &QAction::triggered, this, &KolfWindow::emptySlot);
146 	actionCollection()->setDefaultShortcuts(prevAction, KStandardShortcut::back());
147 	firstAction = actionCollection()->addAction(QStringLiteral("firsthole"));
148 	firstAction->setIcon(QIcon::fromTheme( QStringLiteral( "go-home" )));
149 	firstAction->setText(i18n("&First Hole"));
150 	connect(firstAction, &QAction::triggered, this, &KolfWindow::emptySlot);
151 	actionCollection()->setDefaultShortcuts(firstAction, KStandardShortcut::begin());
152 	lastAction = actionCollection()->addAction(QStringLiteral("lasthole"));
153 	lastAction->setText(i18n("&Last Hole"));
154 	connect(lastAction, &QAction::triggered, this, &KolfWindow::emptySlot);
155 	actionCollection()->setDefaultShortcut(lastAction, Qt::CTRL+Qt::SHIFT+Qt::Key_End); // why not KStandardShortcut::End (Ctrl+End)?
156 	randAction = actionCollection()->addAction(QStringLiteral("randhole"));
157 	randAction->setIcon(QIcon::fromTheme( QStringLiteral( "go-jump" )));
158 	randAction->setText(i18n("&Random Hole"));
159 	connect(randAction, &QAction::triggered, this, &KolfWindow::emptySlot);
160 
161 	// Settings
162 	useMouseAction = new KToggleAction(i18n("Enable &Mouse for Moving Putter"), this);
163 	actionCollection()->addAction(QStringLiteral("usemouse"), useMouseAction);
164 	connect(useMouseAction, &QAction::triggered, this, &KolfWindow::emptySlot);
165 	connect(useMouseAction, &QAction::toggled, this, &KolfWindow::useMouseChanged);
166 	KConfigGroup configGroup(KSharedConfig::openConfig(), "Settings");
167 	useMouseAction->setChecked(configGroup.readEntry("useMouse", true));
168 
169 	useAdvancedPuttingAction = new KToggleAction(i18n("Enable &Advanced Putting"), this);
170 	actionCollection()->addAction(QStringLiteral("useadvancedputting"), useAdvancedPuttingAction);
171 	connect(useAdvancedPuttingAction, &QAction::triggered, this, &KolfWindow::emptySlot);
172 	connect(useAdvancedPuttingAction, &QAction::toggled, this, &KolfWindow::useAdvancedPuttingChanged);
173 	useAdvancedPuttingAction->setChecked(configGroup.readEntry("useAdvancedPutting", false));
174 
175 	showInfoAction = new KToggleAction(QIcon::fromTheme( QStringLiteral( "help-about")), i18n("Show &Info"), this);
176 	actionCollection()->addAction(QStringLiteral("showinfo"), showInfoAction);
177 	connect(showInfoAction, &QAction::triggered, this, &KolfWindow::emptySlot);
178 	actionCollection()->setDefaultShortcut(showInfoAction, Qt::CTRL+Qt::Key_I);
179 	connect(showInfoAction, &QAction::toggled, this, &KolfWindow::showInfoChanged);
180 	showInfoAction->setChecked(configGroup.readEntry("showInfo", true));
181 
182 	showGuideLineAction = new KToggleAction(i18n("Show Putter &Guideline"), this);
183 	actionCollection()->addAction(QStringLiteral("showguideline"), showGuideLineAction);
184 	connect(showGuideLineAction, &QAction::triggered, this, &KolfWindow::emptySlot);
185 	connect(showGuideLineAction, &QAction::toggled, this, &KolfWindow::showGuideLineChanged);
186 	showGuideLineAction->setChecked(configGroup.readEntry("showGuideLine", true));
187 
188 	KToggleAction *act = new KToggleAction(i18n("Enable All Dialog Boxes"), this);
189 	actionCollection()->addAction(QStringLiteral("enableAll"), act);
190 	connect(act, &QAction::triggered, this, &KolfWindow::enableAllMessages);
191 
192 	soundAction = new KToggleAction(i18n("Play &Sounds"), this);
193 	actionCollection()->addAction(QStringLiteral("sound"), soundAction);
194 	connect(soundAction, &QAction::triggered, this, &KolfWindow::emptySlot);
195 	connect(soundAction, &QAction::toggled, this, &KolfWindow::soundChanged);
196 	soundAction->setChecked(configGroup.readEntry("sound", true));
197 
198 	aboutAction = actionCollection()->addAction(QStringLiteral("aboutcourse"));
199 	aboutAction->setText(i18n("&About Course..."));
200 	connect(aboutAction, &QAction::triggered, this, &KolfWindow::emptySlot);
201 	tutorialAction = actionCollection()->addAction(QStringLiteral("tutorial"));
202 	tutorialAction->setText(i18n("&Tutorial"));
203 	connect(tutorialAction, &QAction::triggered, this, &KolfWindow::tutorial);
204 
205 	setupGUI();
206 }
207 
queryClose()208 bool KolfWindow::queryClose()
209 {
210 	if (game)
211 		if (game->askSave(true))
212 			return false;
213 	return true;
214 }
215 
startNewGame()216 void KolfWindow::startNewGame()
217 {
218         NewGameDialog *dialog = nullptr;
219 	int firstHole = 1;
220 
221 	if (loadedGame.isNull())
222 	{
223 		dialog = new NewGameDialog(filename.isNull());
224                 if (dialog->exec() != QDialog::Accepted) {
225                     delete dialog;
226                     return;
227                }
228 	}
229 
230 	players.clear();
231 	delete scoreboard;
232 	scoreboard = new ScoreBoard(dummy);
233 	layout->addWidget(scoreboard, 1, 0);
234 	scoreboard->show();
235 
236 	if (loadedGame.isNull())
237 	{
238 		for (int newId = 1; newId <= dialog->players()->count(); ++newId)
239 		{
240 			players.append(Player());
241 			players.last().ball()->setColor(dialog->players()->at(newId-1)->color());
242 			players.last().setName(dialog->players()->at(newId-1)->name());
243 			players.last().setId(newId);
244 		}
245 
246 		competition = dialog->competition();
247 		filename = filename.isNull()? dialog->course() : filename;
248 	}
249 	else
250 	{
251 		KConfig config(loadedGame);
252 		KConfigGroup configGroup(config.group("0 Saved Game"));
253 
254 		if (isTutorial)
255 			filename = QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("tutorial.kolf"));
256 		else
257 			filename = configGroup.readEntry("Course", QString());
258 
259 		if (filename.isNull())
260 			return;
261 
262 		competition = configGroup.readEntry("Competition", false);
263 		firstHole = configGroup.readEntry("Current Hole", 1);
264 
265 		players.clear();
266 		KolfGame::scoresFromSaved(&config, players);
267 	}
268 
269 	for (PlayerList::Iterator it = players.begin(); it != players.end(); ++it)
270 		scoreboard->newPlayer((*it).name());
271 
272 	delete spacer;
273         spacer = nullptr;
274 	delete game;
275         game = new KolfGame(m_itemFactory, &players, filename, dummy);
276 	game->setStrict(competition);
277 
278 	connect(game, &KolfGame::newHole, scoreboard, &ScoreBoard::newHole);
279 	connect(game, &KolfGame::scoreChanged, scoreboard, &ScoreBoard::setScore);
280 	connect(game, &KolfGame::parChanged, scoreboard, &ScoreBoard::parChanged);
281 	connect(game, &KolfGame::modifiedChanged, this, &KolfWindow::updateModified);
282 	connect(game, &KolfGame::newPlayersTurn, this, &KolfWindow::newPlayersTurn);
283 	connect(game, &KolfGame::holesDone, this, &KolfWindow::gameOver);
284 	connect(game, &KolfGame::checkEditing, this, &KolfWindow::checkEditing);
285 	connect(game, &KolfGame::editingStarted, this, &KolfWindow::editingStarted);
286 	connect(game, &KolfGame::editingEnded, this, &KolfWindow::editingEnded);
287 	connect(game, &KolfGame::inPlayStart, this, &KolfWindow::inPlayStart);
288 	connect(game, &KolfGame::inPlayEnd, this, &KolfWindow::inPlayEnd);
289 	connect(game, &KolfGame::maxStrokesReached, this, &KolfWindow::maxStrokesReached);
290 	connect(game, &KolfGame::largestHole, this, &KolfWindow::updateHoleMenu);
291 	connect(game, &KolfGame::titleChanged, this, &KolfWindow::titleChanged);
292 	connect(game, &KolfGame::newStatusText, this, &KolfWindow::newStatusText);
293         connect(game, qOverload<int>(&KolfGame::currentHole), this,
294                 &KolfWindow::setCurrentHole);
295         connect(holeAction, &QAction::triggered, game,
296                 qOverload<int>(&KolfGame::switchHole));
297         connect(nextAction, &QAction::triggered, game, &KolfGame::nextHole);
298 	connect(prevAction, &QAction::triggered, game, &KolfGame::prevHole);
299 	connect(firstAction, &QAction::triggered, game, &KolfGame::firstHole);
300 	connect(lastAction, &QAction::triggered, game, &KolfGame::lastHole);
301 	connect(randAction, &QAction::triggered, game, &KolfGame::randHole);
302 	connect(editingAction, &QAction::triggered, game, &KolfGame::toggleEditMode);
303 	connect(newHoleAction, &QAction::triggered, game, &KolfGame::addNewHole);
304 	connect(clearHoleAction, &QAction::triggered, game, &KolfGame::clearHole);
305 	connect(resetHoleAction, &QAction::triggered, game, &KolfGame::resetHole);
306 	connect(undoShotAction, &QAction::triggered, game, &KolfGame::undoShot);
307 	//connect(replayShotAction, &QAction::triggered, game, &KolfGame::replay);
308 	connect(aboutAction, &QAction::triggered, game, &KolfGame::showInfoDlg);
309 	connect(useMouseAction, &QAction::toggled, game, &KolfGame::setUseMouse);
310 	connect(useAdvancedPuttingAction, &QAction::toggled, game, &KolfGame::setUseAdvancedPutting);
311 	connect(soundAction, &QAction::toggled, game, &KolfGame::setSound);
312 	connect(showGuideLineAction, &QAction::toggled, game, &KolfGame::setShowGuideLine);
313 	connect(showInfoAction, &QAction::toggled, game, &KolfGame::setShowInfo);
314 
315 	game->setUseMouse(useMouseAction->isChecked());
316 	game->setUseAdvancedPutting(useAdvancedPuttingAction->isChecked());
317 	game->setShowInfo(showInfoAction->isChecked());
318 	game->setShowGuideLine(showGuideLineAction->isChecked());
319 	game->setSound(soundAction->isChecked());
320 
321 	layout->addWidget(game, 0, 0);//, Qt::AlignCenter);
322 
323 	game->show();
324 	game->setFocus();
325 
326 	setEditingEnabled(true);
327 	endAction->setEnabled(true);
328 	setHoleMovementEnabled(true);
329 	setHoleOtherEnabled(true);
330 	aboutAction->setEnabled(true);
331 	highScoreAction->setEnabled(true);
332 	saveAction->setEnabled(true);
333 	saveAsAction->setEnabled(true);
334 	saveGameAction->setEnabled(true);
335 	saveGameAsAction->setEnabled(true);
336 
337 	clearHoleAction->setEnabled(false);
338 	newHoleAction->setEnabled(false);
339 	newAction->setEnabled(false);
340 	loadGameAction->setEnabled(false);
341 	tutorialAction->setEnabled(false);
342 
343 	// so game can do stuff that needs to be done
344 	// after things above are connected
345 	game->startFirstHole(firstHole);
346 	delete dialog;
347 }
348 
newGame()349 void KolfWindow::newGame()
350 {
351 	isTutorial = false;
352 	filename = QString();
353 	startNewGame();
354 }
355 
tutorial()356 void KolfWindow::tutorial()
357 {
358 	QString newfilename = QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("tutorial.kolfgame"));
359 	if (newfilename.isNull())
360 	        return;
361 
362 	filename = QString();
363 	loadedGame = newfilename;
364 	isTutorial = true;
365 
366 	startNewGame();
367 
368 	loadedGame = QString();
369 }
370 
closeGame()371 void KolfWindow::closeGame()
372 {
373 	if (game)
374 	{
375 		if (game->askSave(true))
376 			return;
377 		game->pause();
378 	}
379 
380 	filename = QString();
381 
382 	editingEnded();
383 	delete game;
384 	game = nullptr;
385 	loadedGame = QString();
386 
387 	editingAction->setChecked(false);
388 	setEditingEnabled(false);
389 	endAction->setEnabled(false);
390 	aboutAction->setEnabled(false);
391 	highScoreAction->setEnabled(false);
392 	saveAction->setEnabled(false);
393 	saveAsAction->setEnabled(false);
394 	saveGameAction->setEnabled(false);
395 	saveGameAsAction->setEnabled(false);
396 	setHoleMovementEnabled(false);
397 	setHoleOtherEnabled(false);
398 
399 	clearHoleAction->setEnabled(false);
400 	newHoleAction->setEnabled(false);
401 	newAction->setEnabled(true);
402 	loadGameAction->setEnabled(true);
403 	tutorialAction->setEnabled(true);
404 
405 	titleChanged(QString());
406 	updateModified(false);
407 
408 	QTimer::singleShot(100, this, &KolfWindow::createSpacer);
409 }
410 
createSpacer()411 void KolfWindow::createSpacer()
412 {
413 	// make a player to play the spacer hole
414 	spacerPlayers.clear();
415 	spacerPlayers.append(Player());
416 	spacerPlayers.last().ball()->setColor(Qt::yellow);
417 	spacerPlayers.last().setName(QStringLiteral("player"));
418 	spacerPlayers.last().setId(1);
419 
420 	delete spacer;
421 	spacer = new KolfGame(m_itemFactory, &spacerPlayers, QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("intro")), dummy);
422 	spacer->setSound(false);
423 	layout->addWidget(spacer, 0, 0);//, Qt::AlignCenter);
424 	spacer->ignoreEvents(true);
425 
426 	spacer->show();
427 	spacer->startFirstHole(1);
428 	spacer->hidePutter();
429 }
430 
gameOver()431 void KolfWindow::gameOver()
432 {
433 	int curPar = 0;
434 	int lowScore = INT_MAX; // let's hope it doesn't stay this way!
435 	int curScore = 1;
436 
437 	// names of people who had the lowest score
438 	QStringList names;
439 
440 	HighScoreList highScores;
441 	int scoreBoardIndex = 1;
442 
443 	while (curScore != 0)
444 	{
445 		QString curName;
446 
447 		// name taken as a reference and filled out
448 		curScore = scoreboard->total(scoreBoardIndex, curName);
449 
450 		scoreBoardIndex++;
451 
452 		if (curName == i18n("Par"))
453 		{
454 			curPar = curScore;
455 			curScore = 0;
456 			continue;
457 		}
458 
459 		if (curScore == 0)
460 			continue;
461 
462 		// attempt to add everybody to the highscore list
463 		// (ignored if we aren't competing down below)
464 		highScores.append(HighScore(curName, curScore));
465 
466 		if (curScore < lowScore)
467 		{
468 			names.clear();
469 			lowScore = curScore;
470 			names.append(curName);
471 		}
472 		else if (curScore == lowScore)
473 			names.append(curName);
474 	}
475 
476 	// only announce a winner if more than two entries
477 	// (player and par) are on the scoreboard + one to go past end
478 	// + 1 for koodoo
479 	if (scoreBoardIndex > 4)
480 	{
481 		if (names.count() > 1)
482 		{
483 			QString winners = names.join( i18n(" and " ));
484 			KMessageBox::information(this, i18n("%1 tied", winners));
485 		}
486 		else
487 			KMessageBox::information(this, i18n("%1 won!", names.first()));
488 	}
489 
490 	if (competition)
491 	{
492 		// deal with highscores
493 		// KScoreDialog makes it very easy :-))
494 
495 		KScoreDialog *scoreDialog = new KScoreDialog(KScoreDialog::Name | KScoreDialog::Custom1 | KScoreDialog::Score, this);
496 		scoreDialog->addField(KScoreDialog::Custom1, i18n("Par"), QStringLiteral("Par"));
497 
498 		CourseInfo courseInfo;
499 		game->courseInfo(courseInfo, game->curFilename());
500 
501 		scoreDialog->setConfigGroup(qMakePair(QByteArray(courseInfo.untranslatedName.toUtf8() + " Highscores"), i18n("High Scores for %1", courseInfo.name)));
502 
503 		for (HighScoreList::Iterator it = highScores.begin(); it != highScores.end(); ++it)
504 		{
505 			KScoreDialog::FieldInfo info;
506                         info[KScoreDialog::Name] = (*it).name;
507                         info[KScoreDialog::Score].setNum((*it).score);
508 			info[KScoreDialog::Custom1] = QString::number(curPar);
509 
510                         scoreDialog->addScore(info, KScoreDialog::LessIsMore);
511 		}
512 
513 		scoreDialog->setComment(i18n("High Scores for %1", courseInfo.name));
514 		scoreDialog->show();
515 	}
516 
517 	QTimer::singleShot(700, this, &KolfWindow::closeGame);
518 }
519 
showHighScores()520 void KolfWindow::showHighScores()
521 {
522 	KScoreDialog *scoreDialog = new KScoreDialog(KScoreDialog::Name | KScoreDialog::Custom1 | KScoreDialog::Score, this);
523 	scoreDialog->addField(KScoreDialog::Custom1, i18n("Par"), QStringLiteral("Par"));
524 
525 	CourseInfo courseInfo;
526 	game->courseInfo(courseInfo, game->curFilename());
527 
528 	scoreDialog->setConfigGroup(qMakePair(QByteArray(courseInfo.untranslatedName.toUtf8() + " Highscores"), i18n("High Scores for %1", courseInfo.name)));
529 	scoreDialog->setComment(i18n("High Scores for %1", courseInfo.name));
530 	scoreDialog->show();
531 }
532 
save()533 void KolfWindow::save()
534 {
535 	if (filename.isNull())
536 	{
537 		saveAs();
538 		return;
539 	}
540 
541 	if (game) {
542 		game->save();
543 		game->setFocus();
544 	}
545 }
546 
saveAs()547 void KolfWindow::saveAs()
548 {
549 	QPointer<QFileDialog> fileSaveDialog = new QFileDialog(this);
550 	fileSaveDialog->setWindowTitle(i18nc("@title:window", "Pick Kolf Course to Save To"));
551 	fileSaveDialog->setMimeTypeFilters(QStringList(QStringLiteral("application/x-kourse")));
552 	fileSaveDialog->setAcceptMode(QFileDialog::AcceptSave);
553 	if (fileSaveDialog->exec() == QDialog::Accepted) {
554 		QUrl newfile = fileSaveDialog->selectedUrls().first();
555 		if (!newfile.isEmpty()) {
556 			filename = newfile.toLocalFile();
557 			game->setFilename(filename);
558 			game->save();
559 			game->setFocus();
560 		}
561 	}
562 	delete fileSaveDialog;
563 }
564 
saveGameAs()565 void KolfWindow::saveGameAs()
566 {
567 		QPointer<QFileDialog> fileSaveDialog = new QFileDialog(this);
568 		fileSaveDialog->setWindowTitle(i18nc("@title:window", "Pick Saved Game to Save To"));
569 		fileSaveDialog->setMimeTypeFilters(QStringList(QStringLiteral("application/x-kolf")));
570 		fileSaveDialog->setAcceptMode(QFileDialog::AcceptSave);
571 		if (fileSaveDialog->exec() == QDialog::Accepted) {
572 			QUrl newfile = fileSaveDialog->selectedUrls().first();
573 			if (newfile.isEmpty()) {
574 				return;
575 			}
576 			else {
577 				loadedGame = newfile.toLocalFile();
578 				saveGame();
579 			}
580 		}
581 		delete fileSaveDialog;
582 }
583 
saveGame()584 void KolfWindow::saveGame()
585 {
586 	if (loadedGame.isNull())
587 	{
588 		saveGameAs();
589 		return;
590 	}
591 
592 	KConfig config(loadedGame);
593 	KConfigGroup configGroup(config.group("0 Saved Game"));
594 
595 	configGroup.writeEntry("Competition", competition);
596 	configGroup.writeEntry("Course", filename);
597 
598 	game->saveScores(&config);
599 
600 	configGroup.sync();
601 }
602 
loadGame()603 void KolfWindow::loadGame()
604 {
605 	QPointer<QFileDialog> fileLoadDialog = new QFileDialog(this);
606 	fileLoadDialog->setWindowTitle(i18nc("@title:window", "Pick Kolf Saved Game"));
607 	fileLoadDialog->setMimeTypeFilters(QStringList(QStringLiteral("application/x-kolf")));
608 	fileLoadDialog->setAcceptMode(QFileDialog::AcceptOpen);
609 	fileLoadDialog->setFileMode(QFileDialog::ExistingFile);
610 	if (fileLoadDialog->exec() == QDialog::Accepted) {
611 		loadedGame = fileLoadDialog->selectedUrls().first().toLocalFile();
612 		if (loadedGame.isEmpty()) {
613 			return;
614 		}
615 		else {
616 			isTutorial = false;
617 			startNewGame();
618 		}
619 	}
620 	delete fileLoadDialog;
621 }
622 
623 // called by main for command line files
openUrl(const QUrl & url)624 void KolfWindow::openUrl(const QUrl &url)
625 {
626 	QTemporaryFile tempFile;
627 	tempFile.open();
628 	KIO::FileCopyJob *job = KIO::file_copy(url, QUrl::fromLocalFile(tempFile.fileName()), -1, KIO::Overwrite);
629 	KJobWidgets::setWindow(job, this);
630 	job->exec();
631 	if (!job->error())
632 	{
633 		isTutorial = false;
634 		QMimeDatabase db;
635 		QString mimeType = db.mimeTypeForFile(tempFile).name();
636 		if (mimeType == QLatin1String("application/x-kourse"))
637 			filename = tempFile.fileName();
638 		else if (mimeType == QLatin1String("application/x-kolf"))
639 			loadedGame = tempFile.fileName();
640 		else
641 		{
642 			closeGame();
643 			return;
644 		}
645 
646 		QTimer::singleShot(10, this, &KolfWindow::startNewGame);
647 	}
648 	else
649 		closeGame();
650 }
651 
newPlayersTurn(Player * player)652 void KolfWindow::newPlayersTurn(Player *player)
653 {
654 	tempStatusBarText = i18n("%1's turn", player->name());
655 
656 	if (showInfoAction->isChecked())
657 		statusBar()->showMessage(tempStatusBarText, 5 * 1000);
658 	else
659 		statusBar()->showMessage(tempStatusBarText);
660 
661 	scoreboard->setCurrentCell(player->id() - 1, game->currentHole() - 1);
662 }
663 
newStatusText(const QString & text)664 void KolfWindow::newStatusText(const QString &text)
665 {
666 	if (text.isEmpty())
667 		statusBar()->showMessage(tempStatusBarText);
668 	else
669 		statusBar()->showMessage(text);
670 }
671 
editingStarted()672 void KolfWindow::editingStarted()
673 {
674 	delete editor;
675 	editor = new Editor(m_itemFactory, dummy);
676 	editor->setObjectName( QStringLiteral( "Editor" ) );
677 	connect(editor, &Editor::addNewItem, game, &KolfGame::addNewObject);
678 	connect(editor, &Editor::changed, game, &KolfGame::setModified);
679 	connect(editor, &Editor::addNewItem, this, &KolfWindow::setHoleFocus);
680 	connect(game, &KolfGame::newSelectedItem, editor, &Editor::setItem);
681 
682 	scoreboard->hide();
683 
684 	layout->addWidget(editor, 1, 0);
685 	editor->show();
686 
687 	clearHoleAction->setEnabled(true);
688 	newHoleAction->setEnabled(true);
689 	setHoleOtherEnabled(false);
690 
691 	game->setFocus();
692 }
693 
editingEnded()694 void KolfWindow::editingEnded()
695 {
696 	delete editor;
697 	editor = nullptr;
698 
699 	if (scoreboard)
700 		scoreboard->show();
701 
702 	clearHoleAction->setEnabled(false);
703 	newHoleAction->setEnabled(false);
704 	setHoleOtherEnabled(true);
705 
706 	if (game)
707 		game->setFocus();
708 }
709 
inPlayStart()710 void KolfWindow::inPlayStart()
711 {
712 	setEditingEnabled(false);
713 	setHoleOtherEnabled(false);
714 	setHoleMovementEnabled(false);
715 }
716 
inPlayEnd()717 void KolfWindow::inPlayEnd()
718 {
719 	setEditingEnabled(true);
720 	setHoleOtherEnabled(true);
721 	setHoleMovementEnabled(true);
722 }
723 
maxStrokesReached(const QString & name)724 void KolfWindow::maxStrokesReached(const QString &name)
725 {
726 	KMessageBox::sorry(this, i18n("%1's score has reached the maximum for this hole.", name));
727 }
728 
updateHoleMenu(int largest)729 void KolfWindow::updateHoleMenu(int largest)
730 {
731 	QStringList items;
732 	for (int i = 1; i <= largest; ++i)
733 		items.append(QString::number(i));
734 
735 	// setItems for some reason enables the action
736 	bool shouldbe = holeAction->isEnabled();
737 	holeAction->setItems(items);
738 	holeAction->setEnabled(shouldbe);
739 }
740 
setHoleMovementEnabled(bool yes)741 void KolfWindow::setHoleMovementEnabled(bool yes)
742 {
743 	if (competition)
744 		yes = false;
745 
746 	holeAction->setEnabled(yes);
747 
748 	nextAction->setEnabled(yes);
749 	prevAction->setEnabled(yes);
750 	firstAction->setEnabled(yes);
751 	lastAction->setEnabled(yes);
752 	randAction->setEnabled(yes);
753 }
754 
setHoleOtherEnabled(bool yes)755 void KolfWindow::setHoleOtherEnabled(bool yes)
756 {
757 	if (competition)
758 		yes = false;
759 
760 	resetHoleAction->setEnabled(yes);
761 	undoShotAction->setEnabled(yes);
762 	//replayShotAction->setEnabled(yes);
763 }
764 
setEditingEnabled(bool yes)765 void KolfWindow::setEditingEnabled(bool yes)
766 {
767 	editingAction->setEnabled(competition? false : yes);
768 }
769 
checkEditing()770 void KolfWindow::checkEditing()
771 {
772 	editingAction->setChecked(true);
773 }
774 
updateModified(bool mod)775 void KolfWindow::updateModified(bool mod)
776 {
777 	courseModified = mod;
778 	titleChanged(title);
779 }
780 
titleChanged(const QString & newTitle)781 void KolfWindow::titleChanged(const QString &newTitle)
782 {
783 	title = newTitle;
784 	setCaption(title, courseModified);
785 }
786 
useMouseChanged(bool yes)787 void KolfWindow::useMouseChanged(bool yes)
788 {
789 	KConfigGroup configGroup(KSharedConfig::openConfig(), "Settings"); configGroup.writeEntry("useMouse", yes); configGroup.sync();
790 }
791 
useAdvancedPuttingChanged(bool yes)792 void KolfWindow::useAdvancedPuttingChanged(bool yes)
793 {
794 	KConfigGroup configGroup(KSharedConfig::openConfig(), "Settings"); configGroup.writeEntry("useAdvancedPutting", yes); configGroup.sync();
795 }
796 
showInfoChanged(bool yes)797 void KolfWindow::showInfoChanged(bool yes)
798 {
799 	KConfigGroup configGroup(KSharedConfig::openConfig(), "Settings"); configGroup.writeEntry("showInfo", yes); configGroup.sync();
800 }
801 
showGuideLineChanged(bool yes)802 void KolfWindow::showGuideLineChanged(bool yes)
803 {
804 	KConfigGroup configGroup(KSharedConfig::openConfig(), "Settings"); configGroup.writeEntry("showGuideLine", yes); configGroup.sync();
805 }
806 
soundChanged(bool yes)807 void KolfWindow::soundChanged(bool yes)
808 {
809 	KConfigGroup configGroup(KSharedConfig::openConfig(), "Settings"); configGroup.writeEntry("sound", yes); configGroup.sync();
810 }
811 
enableAllMessages()812 void KolfWindow::enableAllMessages()
813 {
814 	KMessageBox::enableAllMessages();
815 }
816 
setCurrentHole(int hole)817 void KolfWindow::setCurrentHole(int hole)
818 {
819 	if (!holeAction || holeAction->items().count() < hole)
820 		return;
821 	// Golf is 1-based, KListAction is 0-based
822 	holeAction->setCurrentItem(hole - 1);
823 }
824 
825 
826