File indexing completed on 2024-04-28 04:05:04

0001 /*
0002     This file is part of the KDE games library
0003     SPDX-FileCopyrightText: 2001 Andreas Beckermann <b_mann@gmx.de>
0004     SPDX-FileCopyrightText: 2007 Simon Hürlimann <simon.huerlimann@huerlisi.ch>
0005 
0006     SPDX-License-Identifier: LGPL-2.0-only
0007 */
0008 
0009 #include "kgamestandardaction.h"
0010 
0011 // KF
0012 #include <KActionCollection>
0013 #include <KLazyLocalizedString>
0014 #include <KLocalizedString>
0015 #include <KStandardShortcut>
0016 // Qt
0017 #include <QIcon>
0018 // Std
0019 #include <string>
0020 
0021 // Helper class for storing raw data in static tables which can be used for QString instance
0022 // creation at runtime without copying/converting to new memalloc'ed memory, as well as avoiding
0023 // that way storing the strings directly as QStrings resulting in non-constexpr init code on
0024 // library loading
0025 struct RawStringData {
0026     template<std::size_t StringSize>
0027     constexpr inline RawStringData(const char16_t (&_data)[StringSize])
0028         : data(_data)
0029         , size(std::char_traits<char16_t>::length(_data))
0030     {
0031     }
0032     constexpr inline RawStringData(std::nullptr_t)
0033     {
0034     }
0035     constexpr inline RawStringData() = default;
0036 
0037     inline QString toString() const
0038     {
0039         if (!data) {
0040             return QString();
0041         }
0042 
0043         return Qt::Literals::StringLiterals::operator""_s(data, size);
0044     }
0045 
0046 private:
0047     const char16_t *const data = nullptr;
0048     const std::size_t size = 0;
0049 };
0050 
0051 struct KGameStandardActionInfo {
0052     KGameStandardAction::GameStandardAction id;
0053     KStandardShortcut::StandardShortcut globalAccel; // if we reuse a global accel
0054     int shortcut; // specific shortcut (NH: should be configurable)
0055     const RawStringData psName;
0056     const KLazyLocalizedString psLabel;
0057     const KLazyLocalizedString psWhatsThis;
0058     const RawStringData psIconName;
0059     const KLazyLocalizedString psToolTip;
0060 };
0061 
0062 #define CTRL(x) QKeyCombination(Qt::CTRL | Qt::Key_##x).toCombined()
0063 
0064 const KGameStandardActionInfo g_rgActionInfo[] = {
0065     // clang-format off
0066     // "game" menu
0067     { KGameStandardAction::New,             KStandardShortcut::New,       0,                    u"game_new",
0068       kli18nc("new game", "&New"),  kli18n("Start a new game."), u"document-new",        kli18n("Start a new game") },
0069     { KGameStandardAction::Load,            KStandardShortcut::Open,      0,                    u"game_load",
0070       kli18n("&Load..."),           {},                         u"document-open",        kli18n("Open a saved game...") },
0071     { KGameStandardAction::LoadRecent,      KStandardShortcut::AccelNone, 0,                    u"game_load_recent",
0072       kli18n("Load &Recent"),       {},                         nullptr,                 kli18n("Open a recently saved game...") },
0073     { KGameStandardAction::Restart,         KStandardShortcut::Reload,    0,                    u"game_restart",
0074       kli18n("Restart &Game"),      {},                         u"view-refresh",         kli18n("Restart the game") },
0075     { KGameStandardAction::Save,            KStandardShortcut::Save,      0,                    u"game_save",
0076       kli18n("&Save"),              {},                         u"document-save",        kli18n("Save the current game") },
0077     { KGameStandardAction::SaveAs,          KStandardShortcut::AccelNone, 0,                    u"game_save_as",
0078       kli18n("Save &As..."),        {},                         u"document-save-as",     kli18n("Save the current game to another file") },
0079     { KGameStandardAction::End,             KStandardShortcut::End,       0,                    u"game_end",
0080       kli18n("&End Game"),          {},                         u"window-close",         kli18n("End the current game") },
0081     { KGameStandardAction::Pause,           KStandardShortcut::AccelNone, Qt::Key_P,            u"game_pause",
0082       kli18n("Pa&use"),             {},                         u"media-playback-pause", kli18n("Pause the game") },
0083     { KGameStandardAction::Highscores,      KStandardShortcut::AccelNone, CTRL(H),              u"game_highscores",
0084       kli18n("Show &High Scores"),  {},                         u"games-highscores",     kli18n("Show high scores") },
0085     { KGameStandardAction::ClearHighscores, KStandardShortcut::AccelNone, 0,                    u"game_clear_highscores",
0086       kli18n("&Clear High Scores"), {},                         u"clear_highscore",      kli18n("Clear high scores") },
0087     { KGameStandardAction::Statistics,      KStandardShortcut::AccelNone, 0,                    u"game_statistics",
0088       kli18n("Show Statistics"),    {},                         u"highscore",            kli18n("Show statistics") },
0089     { KGameStandardAction::ClearStatistics, KStandardShortcut::AccelNone, 0,                    u"game_clear_statistics",
0090       kli18n("&Clear Statistics"),  {},                         u"flag",                 kli18n("Delete all-time statistics.") },
0091     { KGameStandardAction::Print,           KStandardShortcut::Print,     0,                    u"game_print",
0092       kli18n("&Print..."),          {},                         u"document-print",       {} },
0093     { KGameStandardAction::Quit,            KStandardShortcut::Quit,      0,                    u"game_quit",
0094       kli18n("&Quit"),              {},                         u"application-exit",     kli18n("Quit the program") },
0095 
0096     // "move" menu
0097     { KGameStandardAction::Repeat,  KStandardShortcut::AccelNone, 0,                    u"move_repeat",
0098       kli18n("Repeat"),     {}, nullptr,                kli18n("Repeat the last move") },
0099     { KGameStandardAction::Undo,    KStandardShortcut::Undo,      0,                    u"move_undo",
0100       kli18n("Und&o"),      {}, u"edit-undo",            kli18n("Undo the last move") },
0101     { KGameStandardAction::Redo,    KStandardShortcut::Redo,      0,                    u"move_redo",
0102       kli18n("Re&do"),      {}, u"edit-redo",            kli18n("Redo the latest move") },
0103     { KGameStandardAction::Roll,    KStandardShortcut::AccelNone, CTRL(R),              u"move_roll",
0104       kli18n("&Roll Dice"), {}, u"roll",                 kli18n("Roll the dice") },
0105     { KGameStandardAction::EndTurn, KStandardShortcut::AccelNone, 0,                    u"move_end_turn",
0106       kli18n("End Turn"),   {}, u"games-endturn",        {}  },
0107     { KGameStandardAction::Hint,    KStandardShortcut::AccelNone, Qt::Key_H,            u"move_hint",
0108       kli18n("&Hint"),      {}, u"games-hint",           kli18n("Give a hint") },
0109     { KGameStandardAction::Demo,    KStandardShortcut::AccelNone, Qt::Key_D,            u"move_demo",
0110       kli18n("&Demo"),      {}, u"media-playback-start", kli18n("Play a demo") },
0111     { KGameStandardAction::Solve,   KStandardShortcut::AccelNone, 0,                    u"move_solve",
0112       kli18n("&Solve"),     {}, u"games-solve",          kli18n("Solve the game") },
0113 
0114     // "settings" menu
0115     { KGameStandardAction::Carddecks,           KStandardShortcut::AccelNone, 0, u"options_configure_carddecks",
0116       kli18n("Configure &Carddecks..."),   {}, nullptr, {} },
0117     { KGameStandardAction::ConfigureHighscores, KStandardShortcut::AccelNone, 0, u"options_configure_highscores",
0118       kli18n("Configure &High Scores..."), {}, nullptr, {} },
0119 
0120     { KGameStandardAction::ActionNone, KStandardShortcut::AccelNone, 0, nullptr, {}, {}, nullptr, {} }
0121     // clang-format on
0122 };
0123 
0124 static const KGameStandardActionInfo *infoPtr(KGameStandardAction::GameStandardAction id)
0125 {
0126     for (uint i = 0; g_rgActionInfo[i].id != KGameStandardAction::ActionNone; i++) {
0127         if (g_rgActionInfo[i].id == id)
0128             return &g_rgActionInfo[i];
0129     }
0130     return nullptr;
0131 }
0132 
0133 QAction *KGameStandardAction::_k_createInternal(KGameStandardAction::GameStandardAction id, QObject *parent)
0134 {
0135     QAction *pAction = nullptr;
0136     const KGameStandardActionInfo *pInfo = infoPtr(id);
0137     // qCDebug(GAMES_UI) << "KGameStandardAction::create( " << id << "=" << (pInfo ? pInfo->psName : (const char*)nullptr) << "," << parent << " )";
0138     if (pInfo) {
0139         const QString sLabel = pInfo->psLabel.toString();
0140         switch (id) {
0141         case LoadRecent:
0142             pAction = new KRecentFilesAction(sLabel, parent);
0143             break;
0144         case Pause:
0145         case Demo:
0146             pAction = new KToggleAction(QIcon::fromTheme(pInfo->psIconName.toString()), sLabel, parent);
0147             break;
0148         default:
0149             pAction = new QAction(QIcon::fromTheme(pInfo->psIconName.toString()), sLabel, parent);
0150             break;
0151         }
0152 
0153         // clang-format off
0154         const QList<QKeySequence> cut =
0155             (pInfo->globalAccel != KStandardShortcut::AccelNone) ? KStandardShortcut::shortcut(pInfo->globalAccel) :
0156             (pInfo->shortcut != 0)                               ? QList<QKeySequence>{QKeySequence(pInfo->shortcut)} :
0157             /* else */ QList<QKeySequence>();
0158         // clang-format on
0159 
0160         if (!cut.isEmpty()) {
0161             pAction->setShortcuts(cut);
0162             pAction->setProperty("defaultShortcuts", QVariant::fromValue(cut));
0163         }
0164         if (!pInfo->psToolTip.isEmpty())
0165             pAction->setToolTip(pInfo->psToolTip.toString());
0166         if (!pInfo->psWhatsThis.isEmpty())
0167             pAction->setWhatsThis(pInfo->psWhatsThis.toString());
0168         else if (!pInfo->psToolTip.isEmpty())
0169             pAction->setWhatsThis(pInfo->psToolTip.toString());
0170 
0171         pAction->setObjectName(pInfo->psName.toString());
0172     }
0173 
0174     KActionCollection *collection = qobject_cast<KActionCollection *>(parent);
0175     if (collection && pAction)
0176         collection->addAction(pAction->objectName(), pAction);
0177 
0178     return pAction;
0179 }
0180 
0181 QAction *KGameStandardAction::create(GameStandardAction id, const QObject *recvr, const char *slot, QObject *parent)
0182 {
0183     QAction *pAction = _k_createInternal(id, parent);
0184     if (recvr && slot) {
0185         switch (id) {
0186         case LoadRecent:
0187             QObject::connect(pAction, SIGNAL(urlSelected(QUrl)), recvr, slot);
0188             break;
0189         default:
0190             QObject::connect(pAction, SIGNAL(triggered(bool)), recvr, slot);
0191             break;
0192         }
0193     }
0194     return pAction;
0195 }
0196 
0197 QString KGameStandardAction::name(GameStandardAction id)
0198 {
0199     const KGameStandardActionInfo *pInfo = infoPtr(id);
0200     return (pInfo) ? pInfo->psName.toString() : QString();
0201 }
0202 
0203 QAction *KGameStandardAction::gameNew(const QObject *recvr, const char *slot, QObject *parent)
0204 {
0205     return KGameStandardAction::create(New, recvr, slot, parent);
0206 }
0207 QAction *KGameStandardAction::load(const QObject *recvr, const char *slot, QObject *parent)
0208 {
0209     return KGameStandardAction::create(Load, recvr, slot, parent);
0210 }
0211 KRecentFilesAction *KGameStandardAction::loadRecent(const QObject *recvr, const char *slot, QObject *parent)
0212 {
0213     return static_cast<KRecentFilesAction *>(KGameStandardAction::create(LoadRecent, recvr, slot, parent));
0214 }
0215 QAction *KGameStandardAction::save(const QObject *recvr, const char *slot, QObject *parent)
0216 {
0217     return KGameStandardAction::create(Save, recvr, slot, parent);
0218 }
0219 QAction *KGameStandardAction::saveAs(const QObject *recvr, const char *slot, QObject *parent)
0220 {
0221     return KGameStandardAction::create(SaveAs, recvr, slot, parent);
0222 }
0223 QAction *KGameStandardAction::end(const QObject *recvr, const char *slot, QObject *parent)
0224 {
0225     return KGameStandardAction::create(End, recvr, slot, parent);
0226 }
0227 KToggleAction *KGameStandardAction::pause(const QObject *recvr, const char *slot, QObject *parent)
0228 {
0229     return static_cast<KToggleAction *>(KGameStandardAction::create(Pause, recvr, slot, parent));
0230 }
0231 QAction *KGameStandardAction::highscores(const QObject *recvr, const char *slot, QObject *parent)
0232 {
0233     return KGameStandardAction::create(Highscores, recvr, slot, parent);
0234 }
0235 QAction *KGameStandardAction::statistics(const QObject *recvr, const char *slot, QObject *parent)
0236 {
0237     return KGameStandardAction::create(Highscores, recvr, slot, parent);
0238 }
0239 QAction *KGameStandardAction::clearStatistics(const QObject *recvr, const char *slot, QObject *parent)
0240 {
0241     return KGameStandardAction::create(ClearStatistics, recvr, slot, parent);
0242 }
0243 QAction *KGameStandardAction::print(const QObject *recvr, const char *slot, QObject *parent)
0244 {
0245     return KGameStandardAction::create(Print, recvr, slot, parent);
0246 }
0247 QAction *KGameStandardAction::quit(const QObject *recvr, const char *slot, QObject *parent)
0248 {
0249     return KGameStandardAction::create(Quit, recvr, slot, parent);
0250 }
0251 
0252 QAction *KGameStandardAction::repeat(const QObject *recvr, const char *slot, QObject *parent)
0253 {
0254     return KGameStandardAction::create(Repeat, recvr, slot, parent);
0255 }
0256 QAction *KGameStandardAction::undo(const QObject *recvr, const char *slot, QObject *parent)
0257 {
0258     return KGameStandardAction::create(Undo, recvr, slot, parent);
0259 }
0260 
0261 QAction *KGameStandardAction::redo(const QObject *recvr, const char *slot, QObject *parent)
0262 {
0263     return KGameStandardAction::create(Redo, recvr, slot, parent);
0264 }
0265 
0266 QAction *KGameStandardAction::roll(const QObject *recvr, const char *slot, QObject *parent)
0267 {
0268     return KGameStandardAction::create(Roll, recvr, slot, parent);
0269 }
0270 QAction *KGameStandardAction::endTurn(const QObject *recvr, const char *slot, QObject *parent)
0271 {
0272     return KGameStandardAction::create(EndTurn, recvr, slot, parent);
0273 }
0274 
0275 QAction *KGameStandardAction::carddecks(const QObject *recvr, const char *slot, QObject *parent)
0276 {
0277     return KGameStandardAction::create(Carddecks, recvr, slot, parent);
0278 }
0279 QAction *KGameStandardAction::configureHighscores(const QObject *recvr, const char *slot, QObject *parent)
0280 {
0281     return KGameStandardAction::create(ConfigureHighscores, recvr, slot, parent);
0282 }
0283 QAction *KGameStandardAction::hint(const QObject *recvr, const char *slot, QObject *parent)
0284 {
0285     return KGameStandardAction::create(Hint, recvr, slot, parent);
0286 }
0287 KToggleAction *KGameStandardAction::demo(const QObject *recvr, const char *slot, QObject *parent)
0288 {
0289     return static_cast<KToggleAction *>(KGameStandardAction::create(Demo, recvr, slot, parent));
0290 }
0291 QAction *KGameStandardAction::solve(const QObject *recvr, const char *slot, QObject *parent)
0292 {
0293     return KGameStandardAction::create(Solve, recvr, slot, parent);
0294 }
0295 QAction *KGameStandardAction::restart(const QObject *recvr, const char *slot, QObject *parent)
0296 {
0297     return KGameStandardAction::create(Restart, recvr, slot, parent);
0298 }