KDEGames

kgdifficulty.cpp
1 /***************************************************************************
2  * Copyright 2007 Nicolas Roffet <[email protected]> *
3  * Copyright 2007 Pino Toscano <[email protected]> *
4  * Copyright 2011-2012 Stefan Majewsky <[email protected]> *
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 <QVector>
24 #include <KConfigGroup>
25 #include <KGuiItem>
26 #include <KMessageBox>
27 //the following only used by KgDifficultyGUI
28 #include <KActionCollection>
29 #include <KComboBox>
30 #include <KSelectAction>
31 #include <KXmlGuiWindow>
32 
33 #include <QCoreApplication>
34 #include <QIcon>
35 #include <QGlobalStatic>
36 #include <QStatusBar>
37 
38 #include <KLocalizedString>
39 #include <KSharedConfig>
40 
41 //BEGIN KgDifficultyLevel
42 
43 class KgDifficultyLevel::Private
44 {
45  public:
46  bool m_isDefault;
47  int m_hardness;
48  StandardLevel m_level;
49  QByteArray m_key;
50  QString m_title;
51 
52  Private(int hardness, const QByteArray& key, const QString& title, StandardLevel level, bool isDefault);
53  static Private* fromStandardLevel(StandardLevel level, bool isDefault);
54 };
55 
57  : d(new Private(hardness, key, title, Custom, isDefault))
58 {
59 }
60 
61 KgDifficultyLevel::Private::Private(int hardness, const QByteArray& key, const QString& title, StandardLevel level, bool isDefault)
62  : m_isDefault(isDefault)
63  , m_hardness(hardness)
64  , m_level(level)
65  , m_key(key)
66  , m_title(title)
67 {
68 }
69 
71  : d(Private::fromStandardLevel(level, isDefault))
72 {
73 }
74 
75 KgDifficultyLevel::Private* KgDifficultyLevel::Private::fromStandardLevel(KgDifficultyLevel::StandardLevel level, bool isDefault)
76 {
77  Q_ASSERT_X(level != Custom,
78  "KgDifficultyLevel(StandardLevel) constructor",
79  "Custom level not allowed here"
80  );
81  //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
83  switch (level)
84  {
85  case RidiculouslyEasy:
86  data = qMakePair(QByteArray("Ridiculously Easy"), i18nc("Game difficulty level 1 out of 8", "Ridiculously Easy"));
87  break;
88  case VeryEasy:
89  data = qMakePair(QByteArray("Very Easy"), i18nc("Game difficulty level 2 out of 8", "Very Easy"));
90  break;
91  case Easy:
92  data = qMakePair(QByteArray("Easy"), i18nc("Game difficulty level 3 out of 8", "Easy"));
93  break;
94  case Medium:
95  data = qMakePair(QByteArray("Medium"), i18nc("Game difficulty level 4 out of 8", "Medium"));
96  break;
97  case Hard:
98  data = qMakePair(QByteArray("Hard"), i18nc("Game difficulty level 5 out of 8", "Hard"));
99  break;
100  case VeryHard:
101  data = qMakePair(QByteArray("Very Hard"), i18nc("Game difficulty level 6 out of 8", "Very Hard"));
102  break;
103  case ExtremelyHard:
104  data = qMakePair(QByteArray("Extremely Hard"), i18nc("Game difficulty level 7 out of 8", "Extremely Hard"));
105  break;
106  case Impossible:
107  data = qMakePair(QByteArray("Impossible"), i18nc("Game difficulty level 8 out of 8", "Impossible"));
108  break;
109  case Custom:
110  return nullptr;
111  }
112  return new KgDifficultyLevel::Private(level, data.first, data.second, level, isDefault);
113 }
114 
115 KgDifficultyLevel::~KgDifficultyLevel()
116 {
117  delete d;
118 }
119 
121 {
122  return d->m_isDefault;
123 }
124 
125 int KgDifficultyLevel::hardness() const
126 {
127  return d->m_hardness;
128 }
129 
131 {
132  return d->m_key;
133 }
134 
136 {
137  return d->m_title;
138 }
139 
141 {
142  return d->m_level;
143 }
144 
145 //END KgDifficultyLevel
146 //BEGIN KgDifficulty
147 
148 class KgDifficulty::Private
149 {
150  public:
152  const KgDifficultyLevel* m_currentLevel;
153  bool m_editable, m_gameRunning;
154 
155  Private() : m_currentLevel(nullptr), m_editable(true), m_gameRunning(false) {}
156 };
157 
158 static void saveLevel()
159 {
160  //save current difficulty level in config file (no sync() call here; this
161  //will most likely be called at application shutdown when others are also
162  //writing to KGlobal::config(); also KConfig's dtor will sync automatically)
163  KConfigGroup cg(KSharedConfig::openConfig(), "KgDifficulty");
164  cg.writeEntry("Level", Kg::difficulty()->currentLevel()->key());
165 }
166 
167 KgDifficulty::KgDifficulty(QObject* parent)
168  : QObject(parent)
169  , d(new Private)
170 {
171  qRegisterMetaType<const KgDifficultyLevel*>();
172  qAddPostRoutine(saveLevel);
173 }
174 
176 {
177  qDeleteAll(d->m_levels);
178 }
179 
181 {
182  //The intended use is to create the KgDifficulty object, add levels, *then*
183  //start to work with the currentLevel(). The first call to currentLevel()
184  //will load the previous selection from the config, and the level list will
185  //be considered immutable from this point.
186  Q_ASSERT_X(d->m_currentLevel == nullptr,
187  "KgDifficulty::addLevel",
188  "Only allowed before currentLevel() is called."
189  );
190  //ensure that list stays sorted
192  while (it != d->m_levels.end() && (*it)->hardness() < level->hardness())
193  {
194  ++it;
195  }
196  d->m_levels.insert(it, level);
197  level->setParent(this);
198 }
199 
201 
203 {
204  addLevel(new KgDifficultyLevel(level, isDefault));
205 }
206 
208 {
209  //every level in range != Custom, therefore no level is default
210  addStandardLevelRange(from, to, KgDifficultyLevel::Custom);
211 }
212 
213 void KgDifficulty::addStandardLevelRange(DS from, DS to, DS defaultLevel)
214 {
215  const QVector<DS> levels = QVector<DS>()
216  << KgDifficultyLevel::RidiculouslyEasy
217  << KgDifficultyLevel::VeryEasy
218  << KgDifficultyLevel::Easy
219  << KgDifficultyLevel::Medium
220  << KgDifficultyLevel::Hard
221  << KgDifficultyLevel::VeryHard
222  << KgDifficultyLevel::ExtremelyHard
223  << KgDifficultyLevel::Impossible
224  ;
225  const int fromIndex = levels.indexOf(from);
226  const int toIndex = levels.indexOf(to);
227  const int defaultLevelIndex = levels.indexOf(defaultLevel);
228  Q_ASSERT_X(fromIndex >= 0 && toIndex > fromIndex && (defaultLevelIndex == KgDifficultyLevel::Custom || (defaultLevelIndex >= fromIndex && defaultLevelIndex <= toIndex)),
229  "KgDifficulty::addStandardLevelRange",
230  "No argument may be KgDifficultyLevel::Custom."
231  );
232  for (int i = fromIndex; i <= toIndex; ++i)
233  {
234  addLevel(new KgDifficultyLevel(levels[i], levels[i] == defaultLevel));
235  }
236 }
237 
239 {
240  return d->m_levels;
241 }
242 
244 {
245  if (d->m_currentLevel)
246  {
247  return d->m_currentLevel;
248  }
249  Q_ASSERT(!d->m_levels.isEmpty());
250  //check configuration file for saved difficulty level
251  KConfigGroup cg(KSharedConfig::openConfig(), "KgDifficulty");
252  const QByteArray key = cg.readEntry("Level", QByteArray());
253  for (const KgDifficultyLevel* level : qAsConst(d->m_levels)) {
254  if (level->key() == key)
255  {
256  return d->m_currentLevel = level;
257  }
258  }
259  //no level predefined - look for a default level
260  for (const KgDifficultyLevel* level : qAsConst(d->m_levels)) {
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 
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  Q_EMIT editableChanged(editable);
283 }
284 
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  Q_EMIT gameRunningChanged(gameRunning);
298 }
299 
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(nullptr,
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  Q_EMIT selectedLevelChanged(d->m_currentLevel);
317  return;
318  }
319  }
320  d->m_currentLevel = level;
321  Q_EMIT selectedLevelChanged(level);
322  Q_EMIT currentLevelChanged(level);
323 }
324 
325 //END KgDifficulty
326 
327 Q_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 = nullptr)
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  Q_EMIT signalSelected(d->levels().indexOf(level));
361  }
362  };
363  class Menu : public KSelectAction
364  {
365  Q_OBJECT
366  public:
367  Menu(const QIcon& 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 setCurrentItem(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)));
386  selector, &Selector::slotSelected);
387  QObject::connect(selector, &Selector::signalSelected, selector, &QComboBox::setCurrentIndex);
388 
389  //create menu action
390  const QIcon icon = QIcon::fromTheme(QStringLiteral("games-difficult"));
391  KSelectAction* menu = 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)));
396  QObject::connect(selector, &Selector::signalSelected, menu, &KSelectAction::setCurrentItem);
397 
398  //fill menu and selector
399  const auto levels = difficulty->levels();
400  for (const KgDifficultyLevel* level : levels) {
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(QStringLiteral("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"
standardLevel() returns this for custom levels.
Definition: kgdifficulty.h:46
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)
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)...
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
virtual ~KgDifficulty()
Destroys this instance and all DifficultyLevel instances in it.
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)
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:96
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-2020 The KDE developers.
Generated on Mon Nov 30 2020 22:37:54 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.