KDEGames

kgdifficulty.cpp
1 /*
2  SPDX-FileCopyrightText: 2007 Nicolas Roffet <[email protected]>
3  SPDX-FileCopyrightText: 2007 Pino Toscano <[email protected]>
4  SPDX-FileCopyrightText: 2011-2012 Stefan Majewsky <[email protected]>
5 
6  SPDX-License-Identifier: LGPL-2.0-only
7 */
8 
9 #include "kgdifficulty.h"
10 
11 // KF
12 #include <KConfigGroup>
13 #include <KGuiItem>
14 #include <KMessageBox>
15 #include <KActionCollection>
16 #include <KComboBox>
17 #include <KSelectAction>
18 #include <KXmlGuiWindow>
19 #include <KLocalizedString>
20 #include <KSharedConfig>
21 // Qt
22 #include <QVector>
23 #include <QCoreApplication>
24 #include <QIcon>
25 #include <QGlobalStatic>
26 #include <QStatusBar>
27 
28 //BEGIN KgDifficultyLevel
29 
30 class KgDifficultyLevelPrivate
31 {
32  public:
33  bool m_isDefault;
34  int m_hardness;
36  QByteArray m_key;
37  QString m_title;
38 
39  KgDifficultyLevelPrivate(int hardness, const QByteArray& key, const QString& title,
40  KgDifficultyLevel::StandardLevel level, bool isDefault);
41  static KgDifficultyLevelPrivate* fromStandardLevel(KgDifficultyLevel::StandardLevel level, bool isDefault);
42 };
43 
44 KgDifficultyLevel::KgDifficultyLevel(int hardness, const QByteArray& key, const QString& title, bool isDefault)
45  : d(new KgDifficultyLevelPrivate(hardness, key, title, Custom, isDefault))
46 {
47 }
48 
49 KgDifficultyLevelPrivate::KgDifficultyLevelPrivate(int hardness, const QByteArray& key, const QString& title,
51  : m_isDefault(isDefault)
52  , m_hardness(hardness)
53  , m_level(level)
54  , m_key(key)
55  , m_title(title)
56 {
57 }
58 
60  : d(KgDifficultyLevelPrivate::fromStandardLevel(level, isDefault))
61 {
62 }
63 
64 KgDifficultyLevelPrivate* KgDifficultyLevelPrivate::fromStandardLevel(KgDifficultyLevel::StandardLevel level, bool isDefault)
65 {
66  Q_ASSERT_X(level != KgDifficultyLevel::Custom,
67  "KgDifficultyLevel(StandardLevel) constructor",
68  "Custom level not allowed here"
69  );
70  //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
72  switch (level)
73  {
74  case KgDifficultyLevel::RidiculouslyEasy:
75  data = qMakePair(QByteArray("Ridiculously Easy"), i18nc("Game difficulty level 1 out of 8", "Ridiculously Easy"));
76  break;
77  case KgDifficultyLevel::VeryEasy:
78  data = qMakePair(QByteArray("Very Easy"), i18nc("Game difficulty level 2 out of 8", "Very Easy"));
79  break;
80  case KgDifficultyLevel::Easy:
81  data = qMakePair(QByteArray("Easy"), i18nc("Game difficulty level 3 out of 8", "Easy"));
82  break;
83  case KgDifficultyLevel::Medium:
84  data = qMakePair(QByteArray("Medium"), i18nc("Game difficulty level 4 out of 8", "Medium"));
85  break;
86  case KgDifficultyLevel::Hard:
87  data = qMakePair(QByteArray("Hard"), i18nc("Game difficulty level 5 out of 8", "Hard"));
88  break;
89  case KgDifficultyLevel::VeryHard:
90  data = qMakePair(QByteArray("Very Hard"), i18nc("Game difficulty level 6 out of 8", "Very Hard"));
91  break;
92  case KgDifficultyLevel::ExtremelyHard:
93  data = qMakePair(QByteArray("Extremely Hard"), i18nc("Game difficulty level 7 out of 8", "Extremely Hard"));
94  break;
95  case KgDifficultyLevel::Impossible:
96  data = qMakePair(QByteArray("Impossible"), i18nc("Game difficulty level 8 out of 8", "Impossible"));
97  break;
99  return nullptr;
100  }
101  return new KgDifficultyLevelPrivate(level, data.first, data.second, level, isDefault);
102 }
103 
104 KgDifficultyLevel::~KgDifficultyLevel() = default;
105 
107 {
108  return d->m_isDefault;
109 }
110 
111 int KgDifficultyLevel::hardness() const
112 {
113  return d->m_hardness;
114 }
115 
117 {
118  return d->m_key;
119 }
120 
122 {
123  return d->m_title;
124 }
125 
127 {
128  return d->m_level;
129 }
130 
131 //END KgDifficultyLevel
132 //BEGIN KgDifficulty
133 
134 class KgDifficultyPrivate
135 {
136  public:
138  const KgDifficultyLevel* m_currentLevel;
139  bool m_editable, m_gameRunning;
140 
141  KgDifficultyPrivate() : m_currentLevel(nullptr), m_editable(true), m_gameRunning(false) {}
142 };
143 
144 static void saveLevel()
145 {
146  //save current difficulty level in config file (no sync() call here; this
147  //will most likely be called at application shutdown when others are also
148  //writing to KGlobal::config(); also KConfig's dtor will sync automatically)
149  KConfigGroup cg(KSharedConfig::openConfig(), "KgDifficulty");
150  cg.writeEntry("Level", Kg::difficulty()->currentLevel()->key());
151 }
152 
153 KgDifficulty::KgDifficulty(QObject* parent)
154  : QObject(parent)
155  , d(new KgDifficultyPrivate)
156 {
157  qRegisterMetaType<const KgDifficultyLevel*>();
158  qAddPostRoutine(saveLevel);
159 }
160 
162 {
163  qDeleteAll(d->m_levels);
164 }
165 
167 {
168  //The intended use is to create the KgDifficulty object, add levels, *then*
169  //start to work with the currentLevel(). The first call to currentLevel()
170  //will load the previous selection from the config, and the level list will
171  //be considered immutable from this point.
172  Q_ASSERT_X(d->m_currentLevel == nullptr,
173  "KgDifficulty::addLevel",
174  "Only allowed before currentLevel() is called."
175  );
176  //ensure that list stays sorted
178  while (it != d->m_levels.end() && (*it)->hardness() < level->hardness())
179  {
180  ++it;
181  }
182  d->m_levels.insert(it, level);
183  level->setParent(this);
184 }
185 
187 
189 {
190  addLevel(new KgDifficultyLevel(level, isDefault));
191 }
192 
194 {
195  //every level in range != Custom, therefore no level is default
196  addStandardLevelRange(from, to, KgDifficultyLevel::Custom);
197 }
198 
199 void KgDifficulty::addStandardLevelRange(DS from, DS to, DS defaultLevel)
200 {
201  const QVector<DS> levels = QVector<DS>()
202  << KgDifficultyLevel::RidiculouslyEasy
203  << KgDifficultyLevel::VeryEasy
204  << KgDifficultyLevel::Easy
205  << KgDifficultyLevel::Medium
206  << KgDifficultyLevel::Hard
207  << KgDifficultyLevel::VeryHard
208  << KgDifficultyLevel::ExtremelyHard
209  << KgDifficultyLevel::Impossible
210  ;
211  const int fromIndex = levels.indexOf(from);
212  const int toIndex = levels.indexOf(to);
213  const int defaultLevelIndex = levels.indexOf(defaultLevel);
214  Q_ASSERT_X(fromIndex >= 0 && toIndex > fromIndex && (defaultLevelIndex == KgDifficultyLevel::Custom || (defaultLevelIndex >= fromIndex && defaultLevelIndex <= toIndex)),
215  "KgDifficulty::addStandardLevelRange",
216  "No argument may be KgDifficultyLevel::Custom."
217  );
218  for (int i = fromIndex; i <= toIndex; ++i)
219  {
220  addLevel(new KgDifficultyLevel(levels[i], levels[i] == defaultLevel));
221  }
222 }
223 
225 {
226  return d->m_levels;
227 }
228 
230 {
231  if (d->m_currentLevel)
232  {
233  return d->m_currentLevel;
234  }
235  Q_ASSERT(!d->m_levels.isEmpty());
236  //check configuration file for saved difficulty level
237  KConfigGroup cg(KSharedConfig::openConfig(), "KgDifficulty");
238  const QByteArray key = cg.readEntry("Level", QByteArray());
239  for (const KgDifficultyLevel* level : qAsConst(d->m_levels)) {
240  if (level->key() == key)
241  {
242  return d->m_currentLevel = level;
243  }
244  }
245  //no level predefined - look for a default level
246  for (const KgDifficultyLevel* level : qAsConst(d->m_levels)) {
247  if (level->isDefault())
248  {
249  return d->m_currentLevel = level;
250  }
251  }
252  //no default level predefined - easiest level is probably a sane default
253  return d->m_currentLevel = d->m_levels[0];
254 }
255 
257 {
258  return d->m_editable;
259 }
260 
261 void KgDifficulty::setEditable(bool editable)
262 {
263  if (d->m_editable == editable)
264  {
265  return;
266  }
267  d->m_editable = editable;
268  Q_EMIT editableChanged(editable);
269 }
270 
272 {
273  return d->m_gameRunning;
274 }
275 
276 void KgDifficulty::setGameRunning(bool gameRunning)
277 {
278  if (d->m_gameRunning == gameRunning)
279  {
280  return;
281  }
282  d->m_gameRunning = gameRunning;
283  Q_EMIT gameRunningChanged(gameRunning);
284 }
285 
287 {
288  Q_ASSERT(d->m_levels.contains(level));
289  if (d->m_currentLevel == level)
290  {
291  return;
292  }
293  //ask for confirmation if necessary
294  if (d->m_gameRunning)
295  {
296  const int result = KMessageBox::warningContinueCancel(nullptr,
297  i18n("Changing the difficulty level will end the current game!"),
298  QString(), KGuiItem(i18n("Change the difficulty level"))
299  );
300  if (result != KMessageBox::Continue)
301  {
302  Q_EMIT selectedLevelChanged(d->m_currentLevel);
303  return;
304  }
305  }
306  d->m_currentLevel = level;
307  Q_EMIT selectedLevelChanged(level);
308  Q_EMIT currentLevelChanged(level);
309 }
310 
311 //END KgDifficulty
312 
313 Q_GLOBAL_STATIC(KgDifficulty, g_difficulty)
314 
315 KgDifficulty* Kg::difficulty()
316 {
317  return g_difficulty;
318 }
319 
320 KgDifficultyLevel::StandardLevel Kg::difficultyLevel()
321 {
322  return g_difficulty->currentLevel()->standardLevel();
323 }
324 
325 //BEGIN KgDifficultyGUI
326 
327 namespace KgDifficultyGUI
328 {
329  class Selector : public KComboBox
330  {
331  Q_OBJECT
332  private:
333  KgDifficulty* d;
334  public:
335  Selector(KgDifficulty* difficulty, QWidget* parent = nullptr)
336  : KComboBox(parent), d(difficulty) {}
337  Q_SIGNALS:
338  void signalSelected(int levelIndex);
339  public Q_SLOTS:
340  void slotActivated(int levelIndex)
341  {
342  d->select(d->levels().value(levelIndex));
343  }
344  void slotSelected(const KgDifficultyLevel* level)
345  {
346  Q_EMIT signalSelected(d->levels().indexOf(level));
347  }
348  };
349  class Menu : public KSelectAction
350  {
351  Q_OBJECT
352  public:
353  Menu(const QIcon& i, const QString& s, QWidget* p) : KSelectAction(i,s,p){}
354  public Q_SLOTS:
355  //this whole class just because the following is not a slot
356  void setCurrentItem(int index) { KSelectAction::setCurrentItem(index); }
357  };
358 }
359 
360 void KgDifficultyGUI::init(KXmlGuiWindow* window, KgDifficulty* difficulty)
361 {
362  const bool useSingleton = !difficulty;
363  if (useSingleton)
364  difficulty = Kg::difficulty();
365 
366  //create selector (resides in status bar)
367  KgDifficultyGUI::Selector* selector = new KgDifficultyGUI::Selector(difficulty, window);
368  selector->setToolTip(i18nc("Game difficulty level", "Difficulty"));
369  QObject::connect(selector, QOverload<int>::of(&QComboBox::activated), selector, &Selector::slotActivated);
372  selector, &Selector::slotSelected);
373  QObject::connect(selector, &Selector::signalSelected, selector, &QComboBox::setCurrentIndex);
374 
375  //create menu action
376  const QIcon icon = QIcon::fromTheme(QStringLiteral("games-difficult"));
377  KSelectAction* menu = new KgDifficultyGUI::Menu(icon, i18nc("Game difficulty level", "Difficulty"), window);
378  menu->setToolTip(i18n("Set the difficulty level"));
379  menu->setWhatsThis(i18n("Set the difficulty level of the game."));
380  QObject::connect(menu, &KSelectAction::indexTriggered, selector, &Selector::slotActivated);
382  QObject::connect(selector, &Selector::signalSelected, menu, &KSelectAction::setCurrentItem);
383 
384  //fill menu and selector
385  const auto levels = difficulty->levels();
386  for (const KgDifficultyLevel* level : levels) {
387  selector->addItem(icon, level->title());
388  menu->addAction(level->title());
389  }
390  //initialize selection in selector
391  selector->slotSelected(difficulty->currentLevel());
392 
393  //add selector to statusbar
394  window->statusBar()->addPermanentWidget(selector);
395  //add menu action to window
396  menu->setObjectName(QStringLiteral("options_game_difficulty"));
397  window->actionCollection()->addAction(menu->objectName(), menu);
398 
399  //ensure that the KgDifficulty instance gets deleted
400  if (!useSingleton && !difficulty->parent())
401  {
402  difficulty->setParent(window);
403  }
404 }
405 
406 //END KgDifficultyGUI
407 
408 #include "kgdifficulty.moc"
409 #include "moc_kgdifficulty.cpp"
standardLevel() returns this for custom levels.
Definition: kgdifficulty.h:36
void selectedLevelChanged(const KgDifficultyLevel *level)
Emitted after every call to select(), even when the user has rejected the change. ...
void setEditable(bool editable)
Set whether the difficulty level selection may be edited.
const KgDifficultyLevel * currentLevel() const
virtual KActionCollection * actionCollection() const
int indexOf(const T &value, int from) const const
QStatusBar * statusBar() const const
void addLevel(KgDifficultyLevel *level)
Adds a difficulty level to this instance.
Q_INVOKABLE QAction * addAction(const QString &name, QAction *action)
void writeEntry(const QString &key, const QVariant &value, WriteConfigFlags pFlags=Normal)
KgDifficultyLevel(int hardness, const QByteArray &key, const QString &title, bool isDefault=false)
Refer to the getters&#39; documentation for details on the params.
QList< const KgDifficultyLevel * > levels() const
Q_SIGNALSQ_SIGNALS
void setToolTip(const QString &tip)
T value(int i) const const
int indexOf(const T &value, int from) const const
void setEnabled(bool)
bool setCurrentItem(int index)
ButtonCode warningContinueCancel(QWidget *parent, const QString &text, const QString &caption=QString(), const KGuiItem &buttonContinue=KStandardGuiItem::cont(), const KGuiItem &buttonCancel=KStandardGuiItem::cancel(), const QString &dontAskAgainName=QString(), Options options=Notify)
~KgDifficulty() override
Destroys this instance and all DifficultyLevel instances in it.
void addStandardLevel(KgDifficultyLevel::StandardLevel level, bool isDefault=false)
A shortcut for addLevel(new KgDifficultyLevel(level)).
Q_OBJECTQ_OBJECT
QString i18nc(const char *context, const char *text, const TYPE &arg...)
StandardLevel standardLevel() const
void setObjectName(const QString &name)
void addStandardLevelRange(KgDifficultyLevel::StandardLevel from, KgDifficultyLevel::StandardLevel to)
This convenience method adds a range of standard levels to this instance (including the boundaries)...
void activated(int index)
bool isGameRunning() const
void addAction(QAction *action)
void setWhatsThis(const QString &what)
QList::iterator end()
void setParent(QObject *parent)
bool isDefault() const
static KSharedConfig::Ptr openConfig(const QString &fileName=QString(), OpenFlags mode=FullConfig, QStandardPaths::StandardLocation type=QStandardPaths::GenericConfigLocation)
QString i18n(const char *text, const TYPE &arg...)
void editableChanged(bool editable)
Emitted when the editability changes.
void addPermanentWidget(QWidget *widget, int stretch)
QObject(QObject *parent)
int hardness() const
void insert(int i, const T &value)
void setGameRunning(bool running)
KgDifficulty has optional protection against changing the difficulty level while a game is running...
void setCurrentIndex(int index)
void select(const KgDifficultyLevel *level)
Select a new difficulty level.
QIcon fromTheme(const QString &name)
void indexTriggered(int index)
Q_SLOTSQ_SLOTS
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QObject * parent() const const
KgDifficulty manages difficulty levels of a game in a standard way.
Definition: kgdifficulty.h:86
bool isEditable() const
QList::iterator begin()
void setEnabled(bool)
QByteArray key() const
Q_EMITQ_EMIT
QString title() const
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Tue Dec 7 2021 22:34:15 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.