9#include "kgamehighscoredialog.h"
12#include "../kgamedifficulty.h"
13#include "kgamehighscore.h"
14#include <kdegames_highscore_logging.h>
18#include <KLocalizedString>
22#include <QApplication>
24#include <QDialogButtonBox>
31#include <QStackedWidget>
40class KGameHighScoreDialogPrivate
44 QMap<QByteArray, GroupScores> scores;
45 QList<QByteArray> hiddenGroups;
46 QMap<int, QByteArray> configGroupWeights;
47 QTabWidget *tabWidget;
51 QMap<QByteArray, QList<QStackedWidget *>> stack;
52 QMap<QByteArray, QList<QLabel *>> labels;
57 QPair<QByteArray, int> newName;
58 QPair<QByteArray, int> latest;
61 QByteArray configGroup;
62 KGameHighscore *highscoreObject;
63 QMap<QByteArray, QString> translatedGroupNames;
64 QMap<QByteArray, QWidget *> tabs;
67 QMap<int, QString> header;
68 QMap<int, QString> key;
72 QDialogButtonBox *buttonBox;
74 KGameHighScoreDialog *
const q;
78 explicit KGameHighScoreDialogPrivate(KGameHighScoreDialog *parent)
88 void setupGroup(
const QByteArray &groupName);
91 QString findTranslatedGroupName(
const QByteArray &name);
96 , d_ptr(new KGameHighScoreDialogPrivate(this))
107 d->newName = QPair<QByteArray, int>(
QByteArray(), -1);
108 d->latest = QPair<QByteArray, int>(
"Null", -1);
114 d->header[Name] =
i18nc(
"@title:column",
"Name");
115 d->key[Name] = QStringLiteral(
"Name");
116 d->header[Date] =
i18nc(
"@title:column",
"Date");
117 d->key[Date] = QStringLiteral(
"Date");
118 d->header[Level] =
i18nc(
"@title:column",
"Level");
119 d->key[Level] = QStringLiteral(
"Level");
120 d->header[Score] =
i18nc(
"@title:column",
"Score");
121 d->key[Score] = QStringLiteral(
"Score");
122 d->header[Time] =
i18nc(
"@title:column",
"Time");
123 d->key[Time] = QStringLiteral(
"Time");
142KGameHighScoreDialog::~KGameHighScoreDialog()
146 delete d->highscoreObject;
153 d->configGroup = group.first;
162 if (!d->translatedGroupNames.contains(group.first)) {
163 d->translatedGroupNames.insert(group.first, group.second);
164 qCDebug(KDEGAMES_HIGHSCORE_LOG) <<
"adding" << group.first <<
"->" << group.second;
173 for (; it != groups.
end(); ++it) {
182 const auto levels = diff->
levels();
184 localizedLevelStrings.
insert(level->key(), level->title());
185 levelWeights.
insert(level->hardness(), level->key());
189 if (doSetConfigGroup) {
199 d->hiddenGroups = hiddenGroups;
206 d->configGroupWeights = weights;
209QString KGameHighScoreDialogPrivate::findTranslatedGroupName(
const QByteArray &name)
211 const QString lookupResult = translatedGroupNames.
value(name);
220 d->comment = comment;
228 d->header[field] = header;
236 d->hiddenFields |= field;
242void KGameHighScoreDialogPrivate::setupDialog()
245 for (
int field = 1; field < fields; field = field * 2) {
246 if ((fields & field) && !(hiddenFields & field))
247 col[field] = nrCols++;
251 QList<QByteArray> keysToConfigure = scores.keys();
252 for (
const QByteArray &groupName : std::as_const(configGroupWeights)) {
253 int index = keysToConfigure.
indexOf(groupName);
255 setupGroup(groupName);
259 for (
const QByteArray &groupName : std::as_const(keysToConfigure)) {
260 setupGroup(groupName);
264void KGameHighScoreDialogPrivate::setupGroup(
const QByteArray &groupKey)
266 if (hiddenGroups.contains(groupKey))
268 QWidget *widget =
new QWidget(q);
269 tabs[groupKey] = widget;
271 QString tabName = groupKey.
isEmpty() ?
i18nc(
"@title:tab",
"High Scores") : findTranslatedGroupName(groupKey);
272 tabWidget->addTab(widget, tabName);
274 QGridLayout *layout =
new QGridLayout(widget);
278 layout->
addItem(
new QSpacerItem(0, 15), 4, 0);
280 commentLabel =
new QLabel(tabWidget);
283 QFont bold = q->font();
287 layout->
addItem(
new QSpacerItem(50, 0), 0, 0);
288 label =
new QLabel(
i18nc(
"@title:group",
"Rank"), widget);
290 label->setFont(bold);
292 for (
int field = 1; field < fields; field = field * 2) {
293 if ((fields & field) && !(hiddenFields & field))
295 layout->
addItem(
new QSpacerItem(50, 0), 0, col[field]);
296 label =
new QLabel(header[field], widget);
298 label->setFont(bold);
302 KSeparator *sep =
new KSeparator(
Qt::Horizontal, tabWidget->widget(tabWidget->currentIndex()));
306 for (
int i = 1; i <= 10; ++i) {
309 label =
new QLabel(
i18nc(
"Enumeration (#1, #2 ...) of the highscore entries",
"#%1", num), widget);
310 labels[groupKey].insert((i - 1) * nrCols + 0, label);
312 if (fields & KGameHighScoreDialog::Name)
314 QStackedWidget *localStack =
new QStackedWidget(widget);
315 stack[groupKey].insert(i - 1, localStack);
316 layout->
addWidget(localStack, i + 4, col[KGameHighScoreDialog::Name]);
317 label =
new QLabel(localStack);
318 labels[groupKey].insert((i - 1) * nrCols + col[KGameHighScoreDialog::Name], label);
322 for (
int field = KGameHighScoreDialog::Name * 2; field < fields; field = field * 2) {
323 if ((fields & field) && !(hiddenFields & field))
325 label =
new QLabel(widget);
326 labels[groupKey].insert((i - 1) * nrCols + col[field], label);
336void KGameHighScoreDialogPrivate::aboutToShow()
346 QMap<QByteArray, GroupScores>::const_iterator it = scores.constBegin();
347 for (; it != scores.constEnd(); ++it) {
348 const QByteArray &groupKey = it.key();
349 if (hiddenGroups.contains(groupKey))
351 qCDebug(KDEGAMES_HIGHSCORE_LOG) << latest.first << tabWidget->tabText(tabIndex);
354 if (latest.first == groupKey || (latest.first.isEmpty() && groupKey ==
"High Scores")) {
355 QWidget *widget = tabs.value(groupKey);
356 QGridLayout *layout = qobject_cast<QGridLayout *>(widget->
layout());
358 commentLabel->setText(comment);
359 if (comment.isEmpty()) {
360 commentLabel->setMinimumSize(QSize(1, 1));
361 commentLabel->hide();
362 layout->
addItem(
new QSpacerItem(0, -15), 0, 0);
363 layout->
addItem(
new QSpacerItem(0, -15), 2, 0);
365 layout->
addWidget(commentLabel, 1, 0, 1, nrCols);
366 commentLabel->setMinimumSize(commentLabel->sizeHint());
367 commentLabel->show();
368 layout->
addItem(
new QSpacerItem(0, -10), 0, 0);
369 layout->
addItem(
new QSpacerItem(0, 10), 2, 0);
373 tabWidget->setCurrentWidget(widget);
376 QFont normal = q->font();
381 for (
int i = 1; i <= 10; ++i) {
387 KGameHighScoreDialog::FieldInfo score = scores[groupKey].at(i - 1);
388 label = labels[groupKey].
at((i - 1) * nrCols + 0);
389 if ((i == latest.second) && (groupKey == latest.first))
390 label->setFont(bold);
392 label->setFont(normal);
394 if (fields & KGameHighScoreDialog::Name) {
395 if ((newName.second == i) && (groupKey == newName.first)) {
396 QStackedWidget *localStack = stack[groupKey].at(i - 1);
397 edit =
new KLineEdit(player, localStack);
398 edit->setMinimumWidth(40);
404 label = labels[groupKey].
at((i - 1) * nrCols + col[KGameHighScoreDialog::Name]);
405 if ((i == latest.second) && (groupKey == latest.first))
406 label->setFont(bold);
408 label->setFont(normal);
409 label->setText(score[KGameHighScoreDialog::Name]);
412 for (
int field = KGameHighScoreDialog::Name * 2; field < fields; field = field * 2) {
413 if ((fields & field) && !(hiddenFields & field)) {
414 label = labels[groupKey].
at((i - 1) * nrCols + col[field]);
415 if ((i == latest.second) && (groupKey == latest.first))
416 label->setFont(bold);
418 label->setFont(normal);
419 label->setText(score[field]);
425 int configGroupIndex = tabWidget->indexOf(tabs.value(configGroup));
426 if (!hiddenGroups.contains(configGroup) && configGroupIndex > -1) {
427 tabWidget->setCurrentIndex(configGroupIndex);
429 latest = QPair<QByteArray, int>(QByteArray(), -1);
430 q->setFixedSize(q->minimumSizeHint());
433void KGameHighScoreDialogPrivate::loadScores()
437 QList<QByteArray> groupKeyList;
438 const auto groupStrings = highscoreObject->groupList();
439 for (
const QString &groupString : groupStrings) {
440 groupKeyList << groupString.toUtf8();
443 QByteArray tempCurrentGroup = configGroup;
445 if (!groupKeyList.
contains(configGroup))
447 qCDebug(KDEGAMES_HIGHSCORE_LOG) <<
"The current high score group " << configGroup <<
" isn't in the list, adding it";
448 groupKeyList << configGroup;
449 setupGroup(configGroup);
452 for (
const QByteArray &groupKey : std::as_const(groupKeyList)) {
453 highscoreObject->setHighscoreGroup(QLatin1String(groupKey));
454 player = highscoreObject->readEntry(0, QStringLiteral(
"LastPlayer"));
456 for (
int i = 1; i <= 10; ++i) {
457 KGameHighScoreDialog::FieldInfo score;
458 for (
int field = 1; field < fields; field = field * 2) {
459 if (fields & field) {
460 score[field] = highscoreObject->readEntry(i, key[field], QStringLiteral(
"-"));
463 scores[groupKey].append(score);
466 highscoreObject->setHighscoreGroup(QLatin1String(tempCurrentGroup));
467 const auto groupKeys = scores.keys();
468 for (
const QByteArray &groupKey : groupKeys) {
469 if ((scores[groupKey][0].value(KGameHighScoreDialog::Score) == QLatin1String(
"-")) && (scores.size() > 1) && (latest.first != groupKey)) {
470 qCDebug(KDEGAMES_HIGHSCORE_LOG) <<
"Removing group " << groupKey <<
" since it's unused.";
477void KGameHighScoreDialogPrivate::saveScores()
479 highscoreObject->setHighscoreGroup(QLatin1String(configGroup));
481 highscoreObject->writeEntry(0, QStringLiteral(
"LastPlayer"), player);
483 for (
int i = 1; i <= 10; ++i) {
484 KGameHighScoreDialog::FieldInfo score = scores[configGroup].at(i - 1);
485 for (
int field = 1; field < fields; field = field * 2) {
486 if (fields & field) {
487 highscoreObject->writeEntry(i, key[field], score[field]);
491 highscoreObject->writeAndUnlock();
498 qCDebug(KDEGAMES_HIGHSCORE_LOG) <<
"adding new score";
500 bool askName =
false, lessIsMore =
false;
506 d->latest.first = d->configGroup;
509 d->latest.first =
"Null";
511 for (
int i = 0; i < d->scores[d->configGroup].size(); i++) {
512 FieldInfo score = d->scores[d->configGroup].at(i);
514 int num_score = score[Score].toLong(&ok);
516 score = FieldInfo(newInfo);
517 int newScore = score[Score].toInt();
519 qCDebug(KDEGAMES_HIGHSCORE_LOG) <<
"num_score =" << num_score <<
" - newScore =" << newScore;
521 if (((newScore > num_score) && !lessIsMore) || ((newScore < num_score) && lessIsMore) || !ok) {
522 d->latest = QPair<QByteArray, int>(d->configGroup, i + 1);
523 d->scores[d->configGroup].insert(i, score);
525 d->lastHighPosition = i;
527 if (score[Name].isEmpty())
529 if (!d->player.isEmpty())
531 score[Name] = d->player;
535 if (score[Name].isEmpty()) {
543 d->player = score[Name];
544 d->newName = QPair<QByteArray, int>(d->configGroup, i + 1);
559 d->comment =
i18n(
"Excellent!\nYou have a new high score!");
561 d->comment =
i18n(
"Well done!\nYou made it to the high score list!");
565 d->latest = qMakePair(d->configGroup, 0);
592void KGameHighScoreDialog::slotGotReturn()
598void KGameHighScoreDialog::slotGotName()
602 if (d->newName.second == -1)
605 d->player = d->edit->text();
607 d->scores[d->newName.first][d->newName.second - 1][Name] = d->player;
613 QLabel *
label = d->labels[d->newName.first].
at((d->newName.second - 1) * d->nrCols + d->col[Name]);
614 label->setFont(bold);
615 label->setText(d->player);
616 d->stack[d->newName.first].
at((d->newName.second - 1))->setCurrentWidget(label);
617 d->stack[d->newName.first].at((d->newName.second - 1))->removeWidget(d->edit);
620 d->newName = QPair<QByteArray, int>(QByteArray(), -1);
621 d->scores[d->configGroup].removeAt(10);
623 d->commentLabel->hide();
629void KGameHighScoreDialog::slotForgetScore()
633 if (d->newName.second == -1)
636 d->stack[d->newName.first].at((d->newName.second - 1))->removeWidget(d->edit);
641 d->newName = QPair<QByteArray, int>(QByteArray(), -1);
643 d->scores[d->configGroup].removeAt(d->lastHighPosition);
645 d->commentLabel->hide();
658 if (!d->scores[d->configGroup].isEmpty())
659 return d->scores[d->configGroup].first()[Score].toInt();
664void KGameHighScoreDialog::keyPressEvent(
QKeyEvent *ev)
675#include "moc_kgamehighscoredialog.cpp"
KGameDifficulty manages difficulty levels of a game in a standard way.
QList< const KGameDifficultyLevel * > levels() const
A simple high score implementation.
void setHiddenConfigGroups(const QList< QByteArray > &hiddenGroups)
Hide some config groups so that they are not shown on the dialog (but are still stored in the configu...
int exec() override
Display the dialog as modal.
virtual void show()
Display the dialog as non-modal.
KGameHighScoreDialog(int fields=Name, QWidget *parent=nullptr)
@ LessIsMore
A lower numerical score means higher placing on the table.
@ AskName
Promt the player for their name.
void initFromDifficulty(const KGameDifficulty *difficulty, bool setConfigGroup=true)
Assume that config groups (incl.
void addLocalizedConfigGroupNames(const QMap< QByteArray, QString > &groups)
You must add the translations of all group names to the dialog.
void addField(int field, const QString &header, const QString &key)
Define an extra FieldInfo entry.
void addLocalizedConfigGroupName(const QPair< QByteArray, QString > &group)
You must add the translations of all group names to the dialog.
void setConfigGroupWeights(const QMap< int, QByteArray > &weights)
It is a good idea giving config group weights, otherwise tabs get ordered by their tab name that is n...
void setConfigGroup(const QPair< QByteArray, QString > &group)
The group name must be passed though i18n() in order for the group name to be translated.
void setComment(const QString &comment)
int addScore(const FieldInfo &newInfo=FieldInfo(), AddScoreFlags flags={})
Adds a new score to the list.
QFlags< AddScoreFlag > AddScoreFlags
Stores a combination of AddScoreFlag values.
void hideField(int field)
Hide a field so that it is not shown on the table (but is still stored in the configuration file).
Class for managing highscore tables.
void returnKeyPressed(const QString &text)
QVariant property(UserProperty which) const
QString loginName() const
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
QString label(StandardShortcut id)
bool isEmpty() const const
QByteArray & remove(qsizetype pos, qsizetype len)
QDialog(QWidget *parent, Qt::WindowFlags f)
virtual void keyPressEvent(QKeyEvent *e) override
void setModal(bool modal)
bool testFlag(Enum flag) const const
void setBold(bool enable)
virtual void addItem(QLayoutItem *item) override
bool contains(const AT &value) const const
qsizetype indexOf(const AT &value, qsizetype from) const const
void removeAt(qsizetype i)
iterator insert(const Key &key, const T &value)
T value(const Key &key, const T &defaultValue) const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QObject * parent() const const
const QChar at(qsizetype position) const const
const QChar * constData() const const
bool isEmpty() const const
QString number(double n, char format, int precision)
QString & setNum(double n, char format, int precision)
QString toString() const const