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