1 | /*************************************************************************** |
2 | * Copyright 2007 Nicolas Roffet <nicolas-kde@roffet.com> * |
3 | * Copyright 2007 Pino Toscano <toscano.pino@tiscali.it> * |
4 | * Copyright 2011-2012 Stefan Majewsky <majewsky@gmx.net> * |
5 | * * |
6 | * This program is free software; you can redistribute it and/or modify * |
7 | * it under the terms of the GNU Library General Public License * |
8 | * version 2 as published by the Free Software Foundation * |
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 Library General Public License for more details. * |
14 | * * |
15 | * You should have received a copy of the GNU Library General Public * |
16 | * License along with this program; if not, write to the * |
17 | * Free Software Foundation, Inc., * |
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * |
19 | ***************************************************************************/ |
20 | |
21 | #include "kgdifficulty.h" |
22 | |
23 | #include <QtCore/QVector> |
24 | #include <KDE/KConfigGroup> |
25 | #include <KDE/KGlobal> |
26 | #include <KDE/KGuiItem> |
27 | #include <KDE/KLocale> |
28 | #include <KDE/KMessageBox> |
29 | //the following only used by KgDifficultyGUI |
30 | #include <KDE/KActionCollection> |
31 | #include <KDE/KComboBox> |
32 | #include <KDE/KSelectAction> |
33 | #include <KDE/KStatusBar> |
34 | #include <KDE/KXmlGuiWindow> |
35 | |
36 | //BEGIN KgDifficultyLevel |
37 | |
38 | class KgDifficultyLevel::Private |
39 | { |
40 | public: |
41 | bool m_isDefault; |
42 | int m_hardness; |
43 | StandardLevel m_level; |
44 | QByteArray m_key; |
45 | QString m_title; |
46 | |
47 | Private(int hardness, const QByteArray& key, const QString& title, StandardLevel level, bool isDefault); |
48 | static Private* fromStandardLevel(StandardLevel level, bool isDefault); |
49 | }; |
50 | |
51 | KgDifficultyLevel::KgDifficultyLevel(int hardness, const QByteArray& key, const QString& title, bool isDefault) |
52 | : d(new Private(hardness, key, title, Custom, isDefault)) |
53 | { |
54 | } |
55 | |
56 | KgDifficultyLevel::Private::Private(int hardness, const QByteArray& key, const QString& title, StandardLevel level, bool isDefault) |
57 | : m_isDefault(isDefault) |
58 | , m_hardness(hardness) |
59 | , m_level(level) |
60 | , m_key(key) |
61 | , m_title(title) |
62 | { |
63 | } |
64 | |
65 | KgDifficultyLevel::KgDifficultyLevel(StandardLevel level, bool isDefault) |
66 | : d(Private::fromStandardLevel(level, isDefault)) |
67 | { |
68 | } |
69 | |
70 | KgDifficultyLevel::Private* KgDifficultyLevel::Private::fromStandardLevel(KgDifficultyLevel::StandardLevel level, bool isDefault) |
71 | { |
72 | Q_ASSERT_X(level != Custom, |
73 | "KgDifficultyLevel(StandardLevel) constructor" , |
74 | "Custom level not allowed here" |
75 | ); |
76 | //The first entry in the pair is to be used as a key so don't change it. It doesn't have to match the string to be translated |
77 | QPair<QByteArray, QString> data; |
78 | switch (level) |
79 | { |
80 | case RidiculouslyEasy: |
81 | data = qMakePair(QByteArray("Ridiculously Easy" ), i18nc("Game difficulty level 1 out of 8" , "Ridiculously Easy" )); |
82 | break; |
83 | case VeryEasy: |
84 | data = qMakePair(QByteArray("Very Easy" ), i18nc("Game difficulty level 2 out of 8" , "Very Easy" )); |
85 | break; |
86 | case Easy: |
87 | data = qMakePair(QByteArray("Easy" ), i18nc("Game difficulty level 3 out of 8" , "Easy" )); |
88 | break; |
89 | case Medium: |
90 | data = qMakePair(QByteArray("Medium" ), i18nc("Game difficulty level 4 out of 8" , "Medium" )); |
91 | break; |
92 | case Hard: |
93 | data = qMakePair(QByteArray("Hard" ), i18nc("Game difficulty level 5 out of 8" , "Hard" )); |
94 | break; |
95 | case VeryHard: |
96 | data = qMakePair(QByteArray("Very Hard" ), i18nc("Game difficulty level 6 out of 8" , "Very Hard" )); |
97 | break; |
98 | case ExtremelyHard: |
99 | data = qMakePair(QByteArray("Extremely Hard" ), i18nc("Game difficulty level 7 out of 8" , "Extremely Hard" )); |
100 | break; |
101 | case Impossible: |
102 | data = qMakePair(QByteArray("Impossible" ), i18nc("Game difficulty level 8 out of 8" , "Impossible" )); |
103 | break; |
104 | case Custom: |
105 | return 0; |
106 | } |
107 | return new KgDifficultyLevel::Private(level, data.first, data.second, level, isDefault); |
108 | } |
109 | |
110 | KgDifficultyLevel::~KgDifficultyLevel() |
111 | { |
112 | delete d; |
113 | } |
114 | |
115 | bool KgDifficultyLevel::isDefault() const |
116 | { |
117 | return d->m_isDefault; |
118 | } |
119 | |
120 | int KgDifficultyLevel::hardness() const |
121 | { |
122 | return d->m_hardness; |
123 | } |
124 | |
125 | QByteArray KgDifficultyLevel::key() const |
126 | { |
127 | return d->m_key; |
128 | } |
129 | |
130 | QString KgDifficultyLevel::title() const |
131 | { |
132 | return d->m_title; |
133 | } |
134 | |
135 | KgDifficultyLevel::StandardLevel KgDifficultyLevel::standardLevel() const |
136 | { |
137 | return d->m_level; |
138 | } |
139 | |
140 | //END KgDifficultyLevel |
141 | //BEGIN KgDifficulty |
142 | |
143 | class KgDifficulty::Private |
144 | { |
145 | public: |
146 | QList<const KgDifficultyLevel*> m_levels; |
147 | const KgDifficultyLevel* m_currentLevel; |
148 | bool m_editable, m_gameRunning; |
149 | |
150 | Private() : m_currentLevel(0), m_editable(true), m_gameRunning(false) {} |
151 | }; |
152 | |
153 | KgDifficulty::KgDifficulty(QObject* parent) |
154 | : QObject(parent) |
155 | , d(new Private) |
156 | { |
157 | qRegisterMetaType<const KgDifficultyLevel*>(); |
158 | } |
159 | |
160 | KgDifficulty::~KgDifficulty() |
161 | { |
162 | if (d->m_levels.isEmpty()) |
163 | { |
164 | return; |
165 | } |
166 | //save current difficulty level in config file (no sync() call here; this |
167 | //will most likely be called at application shutdown when others are also |
168 | //writing to KGlobal::config(); also KConfig's dtor will sync automatically) |
169 | KConfigGroup cg(KGlobal::config(), "KgDifficulty" ); |
170 | cg.writeEntry("Level" , currentLevel()->key()); |
171 | //cleanup |
172 | while (!d->m_levels.isEmpty()) |
173 | { |
174 | delete const_cast<KgDifficultyLevel*>(d->m_levels.takeFirst()); |
175 | } |
176 | } |
177 | |
178 | void KgDifficulty::addLevel(KgDifficultyLevel* level) |
179 | { |
180 | //The intended use is to create the KgDifficulty object, add levels, *then* |
181 | //start to work with the currentLevel(). The first call to currentLevel() |
182 | //will load the previous selection from the config, and the level list will |
183 | //be considered immutable from this point. |
184 | Q_ASSERT_X(d->m_currentLevel == 0, |
185 | "KgDifficulty::addLevel" , |
186 | "Only allowed before currentLevel() is called." |
187 | ); |
188 | //ensure that list stays sorted |
189 | QList<const KgDifficultyLevel*>::iterator it = d->m_levels.begin(); |
190 | while (it != d->m_levels.end() && (*it)->hardness() < level->hardness()) |
191 | { |
192 | ++it; |
193 | } |
194 | d->m_levels.insert(it, level); |
195 | level->setParent(this); |
196 | } |
197 | |
198 | typedef KgDifficultyLevel::StandardLevel DS; |
199 | |
200 | void KgDifficulty::addStandardLevel(DS level, bool isDefault) |
201 | { |
202 | addLevel(new KgDifficultyLevel(level, isDefault)); |
203 | } |
204 | |
205 | void KgDifficulty::addStandardLevelRange(DS from, DS to) |
206 | { |
207 | //every level in range != Custom, therefore no level is default |
208 | addStandardLevelRange(from, to, KgDifficultyLevel::Custom); |
209 | } |
210 | |
211 | void KgDifficulty::addStandardLevelRange(DS from, DS to, DS defaultLevel) |
212 | { |
213 | const QVector<DS> levels = QVector<DS>() |
214 | << KgDifficultyLevel::RidiculouslyEasy |
215 | << KgDifficultyLevel::VeryEasy |
216 | << KgDifficultyLevel::Easy |
217 | << KgDifficultyLevel::Medium |
218 | << KgDifficultyLevel::Hard |
219 | << KgDifficultyLevel::VeryHard |
220 | << KgDifficultyLevel::ExtremelyHard |
221 | << KgDifficultyLevel::Impossible |
222 | ; |
223 | const int fromIndex = levels.indexOf(from); |
224 | const int toIndex = levels.indexOf(to); |
225 | const int defaultLevelIndex = levels.indexOf(defaultLevel); |
226 | Q_ASSERT_X(fromIndex >= 0 && toIndex > fromIndex && (defaultLevelIndex == KgDifficultyLevel::Custom || (defaultLevelIndex >= fromIndex && defaultLevelIndex <= toIndex)), |
227 | "KgDifficulty::addStandardLevelRange" , |
228 | "No argument may be KgDifficultyLevel::Custom." |
229 | ); |
230 | for (int i = fromIndex; i <= toIndex; ++i) |
231 | { |
232 | addLevel(new KgDifficultyLevel(levels[i], levels[i] == defaultLevel)); |
233 | } |
234 | } |
235 | |
236 | QList<const KgDifficultyLevel*> KgDifficulty::levels() const |
237 | { |
238 | return d->m_levels; |
239 | } |
240 | |
241 | const KgDifficultyLevel* KgDifficulty::currentLevel() const |
242 | { |
243 | if (d->m_currentLevel) |
244 | { |
245 | return d->m_currentLevel; |
246 | } |
247 | Q_ASSERT(!d->m_levels.isEmpty()); |
248 | //check configuration file for saved difficulty level |
249 | KConfigGroup cg(KGlobal::config(), "KgDifficulty" ); |
250 | const QByteArray key = cg.readEntry("Level" , QByteArray()); |
251 | foreach (const KgDifficultyLevel* level, d->m_levels) |
252 | { |
253 | if (level->key() == key) |
254 | { |
255 | return d->m_currentLevel = level; |
256 | } |
257 | } |
258 | //no level predefined - look for a default level |
259 | foreach (const KgDifficultyLevel* level, d->m_levels) |
260 | { |
261 | if (level->isDefault()) |
262 | { |
263 | return d->m_currentLevel = level; |
264 | } |
265 | } |
266 | //no default level predefined - easiest level is probably a sane default |
267 | return d->m_currentLevel = d->m_levels[0]; |
268 | } |
269 | |
270 | bool KgDifficulty::isEditable() const |
271 | { |
272 | return d->m_editable; |
273 | } |
274 | |
275 | void KgDifficulty::setEditable(bool editable) |
276 | { |
277 | if (d->m_editable == editable) |
278 | { |
279 | return; |
280 | } |
281 | d->m_editable = editable; |
282 | emit editableChanged(editable); |
283 | } |
284 | |
285 | bool KgDifficulty::isGameRunning() const |
286 | { |
287 | return d->m_gameRunning; |
288 | } |
289 | |
290 | void KgDifficulty::setGameRunning(bool gameRunning) |
291 | { |
292 | if (d->m_gameRunning == gameRunning) |
293 | { |
294 | return; |
295 | } |
296 | d->m_gameRunning = gameRunning; |
297 | emit gameRunningChanged(gameRunning); |
298 | } |
299 | |
300 | void KgDifficulty::select(const KgDifficultyLevel* level) |
301 | { |
302 | Q_ASSERT(d->m_levels.contains(level)); |
303 | if (d->m_currentLevel == level) |
304 | { |
305 | return; |
306 | } |
307 | //ask for confirmation if necessary |
308 | if (d->m_gameRunning) |
309 | { |
310 | const int result = KMessageBox::warningContinueCancel(0, |
311 | i18n("Changing the difficulty level will end the current game!" ), |
312 | QString(), KGuiItem(i18n("Change the difficulty level" )) |
313 | ); |
314 | if (result != KMessageBox::Continue) |
315 | { |
316 | emit selectedLevelChanged(d->m_currentLevel); |
317 | return; |
318 | } |
319 | } |
320 | d->m_currentLevel = level; |
321 | emit selectedLevelChanged(level); |
322 | emit currentLevelChanged(level); |
323 | } |
324 | |
325 | //END KgDifficulty |
326 | |
327 | K_GLOBAL_STATIC(KgDifficulty, g_difficulty) |
328 | |
329 | KgDifficulty* Kg::difficulty() |
330 | { |
331 | return g_difficulty; |
332 | } |
333 | |
334 | KgDifficultyLevel::StandardLevel Kg::difficultyLevel() |
335 | { |
336 | return g_difficulty->currentLevel()->standardLevel(); |
337 | } |
338 | |
339 | //BEGIN KgDifficultyGUI |
340 | |
341 | namespace KgDifficultyGUI |
342 | { |
343 | class Selector : public KComboBox |
344 | { |
345 | Q_OBJECT |
346 | private: |
347 | KgDifficulty* d; |
348 | public: |
349 | Selector(KgDifficulty* difficulty, QWidget* parent = 0) |
350 | : KComboBox(parent), d(difficulty) {} |
351 | Q_SIGNALS: |
352 | void signalSelected(int levelIndex); |
353 | public Q_SLOTS: |
354 | void slotActivated(int levelIndex) |
355 | { |
356 | d->select(d->levels().value(levelIndex)); |
357 | } |
358 | void slotSelected(const KgDifficultyLevel* level) |
359 | { |
360 | emit signalSelected(d->levels().indexOf(level)); |
361 | } |
362 | }; |
363 | class : public KSelectAction |
364 | { |
365 | Q_OBJECT |
366 | public: |
367 | (const KIcon& i, const QString& s, QWidget* p) : KSelectAction(i,s,p){} |
368 | public Q_SLOTS: |
369 | //this whole class just because the following is not a slot |
370 | void (int index) { KSelectAction::setCurrentItem(index); } |
371 | }; |
372 | } |
373 | |
374 | void KgDifficultyGUI::init(KXmlGuiWindow* window, KgDifficulty* difficulty) |
375 | { |
376 | const bool useSingleton = !difficulty; |
377 | if (useSingleton) |
378 | difficulty = Kg::difficulty(); |
379 | |
380 | //create selector (resides in status bar) |
381 | KgDifficultyGUI::Selector* selector = new KgDifficultyGUI::Selector(difficulty, window); |
382 | selector->setToolTip(i18nc("Game difficulty level" , "Difficulty" )); |
383 | QObject::connect(selector, SIGNAL(activated(int)), selector, SLOT(slotActivated(int))); |
384 | QObject::connect(difficulty, SIGNAL(editableChanged(bool)), selector, SLOT(setEnabled(bool))); |
385 | QObject::connect(difficulty, SIGNAL(selectedLevelChanged(const KgDifficultyLevel*)), |
386 | selector, SLOT(slotSelected(const KgDifficultyLevel*))); |
387 | QObject::connect(selector, SIGNAL(signalSelected(int)), selector, SLOT(setCurrentIndex(int))); |
388 | |
389 | //create menu action |
390 | const KIcon icon("games-difficult" ); |
391 | KSelectAction* = new KgDifficultyGUI::Menu(icon, i18nc("Game difficulty level" , "Difficulty" ), window); |
392 | menu->setToolTip(i18n("Set the difficulty level" )); |
393 | menu->setWhatsThis(i18n("Set the difficulty level of the game." )); |
394 | QObject::connect(menu, SIGNAL(triggered(int)), selector, SLOT(slotActivated(int))); |
395 | QObject::connect(difficulty, SIGNAL(editableChanged(bool)), menu, SLOT(setEnabled(bool))); |
396 | QObject::connect(selector, SIGNAL(signalSelected(int)), menu, SLOT(setCurrentItem(int))); |
397 | |
398 | //fill menu and selector |
399 | foreach (const KgDifficultyLevel* level, difficulty->levels()) |
400 | { |
401 | selector->addItem(icon, level->title()); |
402 | menu->addAction(level->title()); |
403 | } |
404 | //initialize selection in selector |
405 | selector->slotSelected(difficulty->currentLevel()); |
406 | |
407 | //add selector to statusbar |
408 | window->statusBar()->addPermanentWidget(selector); |
409 | //add menu action to window |
410 | menu->setObjectName(QLatin1String("options_game_difficulty" )); |
411 | window->actionCollection()->addAction(menu->objectName(), menu); |
412 | |
413 | //ensure that the KgDifficulty instance gets deleted |
414 | if (!useSingleton && !difficulty->parent()) |
415 | { |
416 | difficulty->setParent(window); |
417 | } |
418 | } |
419 | |
420 | //END KgDifficultyGUI |
421 | |
422 | #include "kgdifficulty.moc" |
423 | #include "moc_kgdifficulty.cpp" |
424 | |