File indexing completed on 2024-04-21 04:05:09

0001 /*
0002     SPDX-FileCopyrightText: 2008 Ian Wadham <iandw.au@gmail.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 // Own header
0008 #include "kubrick.h"
0009 
0010 // Qt includes.
0011 #include <QAction>
0012 #include <QActionGroup>
0013 #include <QHBoxLayout>
0014 #include <QLineEdit>
0015 #include <QStatusBar>
0016 #include <QString>
0017 
0018 // KDE includes.
0019 #include <KActionCollection>
0020 #include <KLocalizedString>
0021 #include <KMessageBox>
0022 #include <KGameStandardAction>
0023 #include <KToggleAction>
0024 
0025 // Local includes.
0026 #include "game.h"
0027 #include "gameglview.h"
0028 
0029 Kubrick::Kubrick () :
0030     singmasterMoves (nullptr)
0031 {
0032     // Window title.
0033     // setWindowTitle("Rubik's Cube");  // DELETED - This is a *trademark*.
0034 
0035     // use multi-sample (anti-aliased) OpenGL if available
0036     /* Disable it for now, was causing black screens in some configurations
0037     QGLFormat defFormat = QGLFormat::defaultFormat();
0038     defFormat.setSampleBuffers(true);
0039     QGLFormat::setDefaultFormat(defFormat);
0040     */
0041 
0042     game     = new Game     (this);
0043 
0044     gameView = new GameGLView (game, this);
0045 
0046     // Set the view as the central widget
0047     setCentralWidget(gameView);
0048 
0049     // Set up the menus, keystrokes, etc.
0050     initGUI();
0051 
0052     // Enable the help menu.
0053     setHelpMenuEnabled (true);
0054 
0055     // Load the GUI from the kubrickui.rc file.
0056     setupGUI ();
0057 
0058      // Demos menu. This needs to be after setupGUI() call.
0059     auto slot = &Kubrick::patternSelected;
0060     Q_UNUSED(slot)
0061     fillDemoList (patterns, patternList,
0062             QStringLiteral("patterns_list"), &Kubrick::patternSelected);
0063     fillDemoList (solvingMoves, movesList,
0064             QStringLiteral("demo_moves_list"), &Kubrick::movesSelected);
0065 
0066     // Set up a status bar.
0067     statusBar()->show ();
0068     statusBarLabel = new QLabel(i18n("Welcome to Kubrick"), this);
0069     statusBarLabel->setAlignment (Qt::AlignCenter);
0070     statusBar()->addWidget (statusBarLabel, 1);
0071 
0072     // Set a larger font than toolbar-default for the Singmaster-moves display.
0073     QFont f = statusBar()->font();
0074     f.setPointSize (f.pointSize()); // Needed to force a size-change.
0075     singmasterLabel->setFont (f);
0076     singmasterMoves->setFont (f);
0077 
0078     // Start the game with a randomised demo.
0079     game->initGame (gameView, this);
0080 }
0081 
0082 
0083 Kubrick::~Kubrick ()
0084 {
0085 }
0086 
0087 
0088 const Kubrick::PuzzleItem Kubrick::easyItems [] = {
0089     {kli18n("2x2x1 mat, 1 move"),    2, 2, 1, 1, 1, 1},
0090     {kli18n("2x2x1 mat, 2 moves"),   2, 2, 1, 2, 1, 1},
0091     {kli18n("2x2x1 mat, 3 moves"),   2, 2, 1, 3, 1, 1},
0092     {kli18n("2x2x2 cube, 2 moves"),  2, 2, 2, 2, 1, 1},
0093     {kli18n("2x2x2 cube, 3 moves"),  2, 2, 2, 3, 1, 1},
0094     {kli18n("2x2x2 cube, 4 moves"),  2, 2, 2, 4, 1, 0},
0095     {kli18n("3x3x1 mat, 4 moves"),   3, 3, 1, 4, 1, 0},
0096     {kli18n("3x3x3 cube, 3 moves"),  3, 3, 3, 3, 1, 0},
0097     {kli18n("3x3x3 cube, 4 moves"),  3, 3, 3, 4, 1, 0},
0098     {KLazyLocalizedString(),                0, 0, 0, 0, 0, 0}
0099 };
0100 
0101 
0102 const Kubrick::PuzzleItem Kubrick::notSoEasyItems [] = {
0103     {kli18n("3x3x3 cube, 3 moves"),  3, 3, 3, 3, 0, 0},
0104     {kli18n("3x3x3 cube, 4 moves"),  3, 3, 3, 4, 0, 0},
0105     {kli18n("4x4x4 cube, 4 moves"),  4, 4, 4, 4, 0, 0},
0106     {kli18n("5x5x5 cube, 4 moves"),  5, 5, 5, 4, 0, 0},
0107     {kli18n("6x3x2 brick, 4 moves"), 6, 3, 2, 4, 0, 0},
0108     {KLazyLocalizedString(),                0, 0, 0, 0, 0, 0}
0109 };
0110 
0111 
0112 const Kubrick::PuzzleItem Kubrick::hardItems [] = {
0113     {kli18n("3x3x3 cube, 7 moves"),  3, 3, 3, 7, 0, 0},
0114     {kli18n("4x4x4 cube, 5 moves"),  4, 4, 4, 5, 0, 0},
0115     {kli18n("5x5x5 cube, 6 moves"),  5, 5, 5, 6, 0, 0},
0116     {kli18n("6x6x6 cube, 6 moves"),  6, 6, 6, 6, 0, 0},
0117     {kli18n("6x4x1 mat, 9 moves"),   6, 4, 1, 9, 0, 0},
0118     {kli18n("6x3x2 brick, 6 moves"), 6, 3, 2, 7, 0, 0},
0119     {KLazyLocalizedString(),                0, 0, 0, 0, 0, 0}
0120 };
0121 
0122 
0123 const Kubrick::PuzzleItem Kubrick::veryHardItems [] = {
0124     {kli18n("3x3x3 cube, 12 moves"), 3, 3, 3, 12, 0, 0},
0125     {kli18n("3x3x3 cube, 15 moves"), 3, 3, 3, 15, 0, 0},
0126     {kli18n("3x3x3 cube, 20 moves"), 3, 3, 3, 20, 0, 0},
0127     {kli18n("4x4x4 cube, 12 moves"), 4, 4, 4, 12, 0, 0},
0128     {kli18n("5x5x5 cube, 15 moves"), 5, 5, 5, 15, 0, 0},
0129     {kli18n("6x6x6 cube, 25 moves"), 6, 6, 6, 25, 0, 0},
0130     {KLazyLocalizedString(),                0, 0, 0,  0, 0, 0}
0131 };
0132 
0133 
0134 const KLazyLocalizedString Kubrick::patternMovesInfo = kli18n(
0135     "Rubik's Cube can be moved into many interesting patterns.  Here are "
0136     "a few from David Singmaster's classic book 'Notes on Rubik's Magic Cube, "
0137     "Fifth Edition', pages 47-49, published in 1981.  After a pattern has "
0138     "formed, you can use the Solve action (default key S) to undo and redo it "
0139     "as often as you like."
0140     );
0141 
0142 
0143 const Kubrick::DemoItem Kubrick::patterns [] = {
0144     {QString(),                             kli18n("Info")},
0145     {QStringLiteral("p333X6.kbk"),      kli18n("3x3x3, 6 X")},
0146     {QStringLiteral("p333X2.kbk"),      kli18n("3x3x3, 2 X")},
0147     {QStringLiteral("p333Spot6.kbk"),       kli18n("3x3x3, 6 Spot")},
0148     {QStringLiteral("p333Spot4.kbk"),       kli18n("3x3x3, 4 Spot")},
0149     {QStringLiteral("p333Plus4.kbk"),       kli18n("3x3x3, 4 Plus")},
0150     {QStringLiteral("p333Bar4.kbk"),        kli18n("3x3x3, 4 Bar")},
0151     {QStringLiteral("p333U6.kbk"),      kli18n("3x3x3, 6 U")},
0152     {QStringLiteral("p333U4.kbk"),      kli18n("3x3x3, 4 U")},
0153     {QStringLiteral("p333Snake.kbk"),       kli18n("3x3x3, Snake")},
0154     {QStringLiteral("p333Worm.kbk"),        kli18n("3x3x3, Worm")},
0155     {QStringLiteral("p333Tricolor6.kbk"),   kli18n("3x3x3, Tricolor")},
0156     {QStringLiteral("p333DoubleCube.kbk"),  kli18n("3x3x3, Double Cube")},
0157     {QStringLiteral("END"),         KLazyLocalizedString()}
0158 };
0159 
0160 
0161 const KLazyLocalizedString Kubrick::solvingMovesInfo = kli18n(
0162     "<qt>Mathematicians calculate that a 3x3x3 cube can be shuffled into "
0163     "43,252,003,274,489,856,000 different patterns, yet they conjecture "
0164     "that all positions can be solved in 20 moves or less.  The method "
0165     "that can do that (as yet undiscovered) is called God's Algorithm."
0166     "<br><br>"
0167     "Many longer methods are known.  See the two Wikipedia articles on "
0168     "Rubik's Cube and Optimal Solutions for Rubik's Cube."
0169     "<br><br>"
0170     "Several methods work systematically by building the solution one layer "
0171     "at a time, using sequences of moves that solve a few pieces without "
0172     "disturbing what has already been done.  The 'Beginner Solution' "
0173     "demonstrated here uses that approach.  Just over 100 moves solve a cube "
0174     "that is shuffled in 20.</qt>"
0175     );
0176 
0177 
0178 const Kubrick::DemoItem Kubrick::solvingMoves [] = {
0179     {QString(),                         kli18n("Info")},
0180     {QStringLiteral("m333Layer1.kbk"),      kli18n("3x3x3 Layer 1, Edges First")},
0181     {QStringLiteral("m333MEdge1.kbk"),      kli18n("3x3x3 Layer 2, Edge from Bottom Right")},
0182     {QStringLiteral("m333MEdge2.kbk"),      kli18n("3x3x3 Layer 2, Edge from Bottom Left")},
0183     {QStringLiteral("m333LLEdgeFlip.kbk"),  kli18n("3x3x3 Layer 3, Flip Edge Pieces")},
0184     {QStringLiteral("m333LLCornerPos.kbk"), kli18n("3x3x3 Layer 3, Place Corners")},
0185     {QStringLiteral("m333LLCornerRot.kbk"), kli18n("3x3x3 Layer 3, Twist Corners")},
0186     {QStringLiteral("m333LLEdgePos.kbk"),   kli18n("3x3x3 Layer 3, Place Edges and DONE!")},
0187     {QStringLiteral("m333Complete.kbk"),    kli18n("3x3x3 Cube, Complete Solution")},
0188     {QStringLiteral("m333E2prX.kbk"),       kli18n("3x3x3 Swap 2 Pairs of Edges")},
0189     {QStringLiteral("m333CTwirl2.kbk"),     kli18n("3x3x3 Untwist 2 Corners")},
0190     {QStringLiteral("m333EFlip2.kbk"),      kli18n("3x3x3 Flip 2 Edges")},
0191     {QStringLiteral("END"),         KLazyLocalizedString()}
0192 };
0193 
0194 
0195 // Create an action and set some help text for all menu items
0196 // See also the *ui.rc file for the XML definitions
0197 void Kubrick::initGUI()
0198 {
0199     // Game menu.
0200     QAction * newAction =   KGameStandardAction::gameNew (
0201                 game, &Game::newPuzzle, this);
0202     actionCollection()->addAction (newAction->objectName(), newAction);
0203     newAction->setText      (i18n("&New Puzzle"));
0204     newAction->setToolTip   (i18n("Start a new puzzle."));
0205     newAction->setWhatsThis (i18n("Finish the puzzle you are working on "
0206                 "and start a new puzzle with the same "
0207                 "dimensions and number of shuffling moves."));
0208 
0209     QAction *
0210     a =             KGameStandardAction::load (
0211                 game, &Game::load, this);
0212     actionCollection()->addAction (a->objectName(), a);
0213     a->setText          (i18n("&Load Puzzle..."));
0214     a->setToolTip       (i18n("Reload a saved puzzle from a file."));
0215     a->setWhatsThis     (i18n("Reload a puzzle you have previously "
0216                 "saved on a file, including its dimensions, "
0217                 "settings, current state and history of "
0218                 "moves."));
0219 
0220     a =             KGameStandardAction::save (
0221                 game, &Game::save, this);
0222     actionCollection()->addAction (a->objectName(), a);
0223     a->setText          (i18n("&Save Puzzle..."));
0224     a->setToolTip       (i18n("Save the puzzle on a file."));
0225     a->setWhatsThis     (i18n("Save the puzzle on a file, including "
0226                 "its dimensions, settings, current state and "
0227                 "history of moves."));
0228 
0229     a =             KGameStandardAction::saveAs (
0230                 game, &Game::saveAs, this);
0231     actionCollection()->addAction (a->objectName(), a);
0232     a->setText          (i18n("&Save Puzzle As..."));
0233 
0234     a =             KGameStandardAction::
0235                 restart (game, &Game::undoAll, this);
0236     actionCollection()->addAction (a->objectName(), a);
0237     a->setText          (i18n("Restart &Puzzle..."));
0238     a->setToolTip       (i18n("Undo all previous moves and start "
0239                 "again."));
0240     a->setWhatsThis     (i18n("Undo all previous moves and start "
0241                 "again."));
0242 
0243     a =             KGameStandardAction::
0244                 quit (this, &Kubrick::close, this);
0245     actionCollection()->addAction (a->objectName(), a);
0246     // NOTE: KXmlGuiWindow::close() calls Kubrick::queryClose(), our real "quit"
0247 
0248     // Move menu.
0249     a =             KGameStandardAction::
0250                 undo (game, &Game::undoMove, this);
0251     actionCollection()->addAction (a->objectName(), a);
0252     a->setToolTip       (i18n("Undo the last move."));
0253     a->setWhatsThis     (i18n("Undo the last move."));
0254 
0255     a =             KGameStandardAction::
0256                 redo (game, &Game::redoMove, this);
0257     actionCollection()->addAction (a->objectName(), a);
0258     a->setToolTip       (i18n("Redo a previously undone move."));
0259     a->setWhatsThis     (i18n("Redo a previously undone move "
0260                 "(repeatedly from the start if required)."));
0261 
0262     a =             KGameStandardAction::
0263                 solve (game, &Game::solveCube, this);
0264     actionCollection()->addAction (a->objectName(), a);
0265     a->setToolTip       (i18n("Show the solution of the puzzle."));
0266     a->setWhatsThis     (i18n("Show the solution of the puzzle by "
0267                 "undoing and re-doing all shuffling moves."));
0268 
0269     a =             KGameStandardAction::
0270                 demo (game, &Game::toggleDemo, this);
0271     actionCollection()->addAction (a->objectName(), a);
0272     a->setText          (i18n("Main &Demo"));
0273     a->setToolTip       (i18n("Run a demonstration of puzzle moves."));
0274     a->setWhatsThis     (i18n("Run a demonstration of puzzle moves, "
0275                 "in which randomly chosen cubes, bricks or "
0276                 "mats are shuffled and solved."));
0277 
0278     a = actionCollection()->addAction ( QStringLiteral( "standard_view" ));
0279     a->setText          (i18n("Realign Cube"));
0280     a->setToolTip       (i18n("Realign the cube so that the top, "
0281                 "front and right faces are visible together."));
0282     a->setWhatsThis     (i18n("Realign the cube so that the top, "
0283                 "front and right faces are visible together "
0284                 "and the cube's axes are parallel to the XYZ "
0285                 "axes, thus making keyboard moves properly "
0286                 "meaningful."));
0287     a->setIcon          (QIcon::fromTheme( QStringLiteral( "go-home" )));
0288     KActionCollection::setDefaultShortcut(a, Qt::Key_Home);
0289     connect (a, &QAction::triggered, game, &Game::setStandardView);
0290 
0291     a = actionCollection()->addAction ( QStringLiteral( "redo_all" ));
0292     a->setText (i18n("Redo All"));
0293     KActionCollection::setDefaultShortcut(a, Qt::SHIFT | Qt::Key_R);
0294     connect (a, &QAction::triggered, game, &Game::redoAll);
0295 
0296     // Read-only display of Singmaster moves on the toolbar.
0297     singmasterLabel = new QLabel (i18n("Singmaster Moves"), this);
0298     singmasterMoves = new QLineEdit (this);
0299 
0300     QWidget *sigmasterWidget = new QWidget(this);
0301     QHBoxLayout *sigmasterLayout = new QHBoxLayout (sigmasterWidget);
0302     sigmasterLayout->setContentsMargins(0, 0, 0, 0);
0303     sigmasterLayout->addWidget(singmasterLabel);
0304     sigmasterLayout->addWidget(singmasterMoves);
0305 
0306     QWidgetAction *w = new QWidgetAction (this);
0307     actionCollection()->addAction ( QStringLiteral( "singmaster_label" ), w);
0308     w->setDefaultWidget (sigmasterWidget);
0309 
0310     actionCollection()->addAction ( QStringLiteral( "singmaster_moves" ), w);
0311     w->setText (i18n("Singmaster Moves"));
0312 
0313     QString singmasterToolTip = i18n("This area shows Singmaster moves.");
0314     QString singmasterWhatsThis = i18nc("The letters RLFBUD are mathematical "
0315             "notation based on English words. Please leave those "
0316             "letters and words untranslated in some form.",
0317 
0318             "This area shows Singmaster moves. "
0319             "They are based on the letters RLFBUD, representing "
0320             "(in English) the Right, Left, Front, Back, Up and "
0321             "Down faces. In normal view, the letters RFU represent "
0322             "clockwise moves of the three visible faces and LBD "
0323             "appear as anticlockwise moves of the hidden faces. "
0324             "Adding a ' (apostrophe) to a letter gives the reverse "
0325             "of that letter's move. To move inner slices, add "
0326             "periods (or dots) before the letter of the nearest "
0327             "face.");
0328 
0329     singmasterLabel->setToolTip (singmasterToolTip);
0330     singmasterLabel->setWhatsThis (singmasterWhatsThis);
0331     singmasterMoves->setToolTip (singmasterToolTip);
0332     singmasterMoves->setWhatsThis (singmasterWhatsThis);
0333 
0334     singmasterMoves->setReadOnly (true);
0335     singmasterLabel->setBuddy (singmasterMoves);
0336     singmasterMoves->show();
0337     singmasterLabel->show();
0338     singmasterMoves->setFocusPolicy (Qt::NoFocus);
0339     singmasterMoves->clearFocus();
0340 
0341     // "Choose Puzzle Type" sub-menu.
0342     easyList = new KSelectAction (i18n("&Easy"), this);
0343     actionCollection()->addAction ( QStringLiteral( "easy_list" ), easyList);
0344     fillPuzzleList (easyList, easyItems);
0345     connect(easyList, &KSelectAction::indexTriggered, this, &Kubrick::easySelected);
0346 
0347     notSoEasyList = new KSelectAction (i18n("&Not So Easy"), this);
0348     actionCollection()->addAction ( QStringLiteral( "not_easy_list" ), notSoEasyList);
0349     fillPuzzleList (notSoEasyList, notSoEasyItems);
0350     connect(notSoEasyList, &KSelectAction::indexTriggered, this, &Kubrick::notSoEasySelected);
0351 
0352     hardList = new KSelectAction (i18n("&Hard"), this);
0353     actionCollection()->addAction ( QStringLiteral( "hard_list" ), hardList);
0354     fillPuzzleList (hardList, hardItems);
0355     connect(hardList, &KSelectAction::indexTriggered, this, &Kubrick::hardSelected);
0356 
0357     veryHardList = new KSelectAction (i18n("&Very Hard"), this);
0358     actionCollection()->addAction ( QStringLiteral( "very_hard_list" ), veryHardList);
0359     fillPuzzleList (veryHardList, veryHardItems);
0360     connect(veryHardList, &KSelectAction::indexTriggered, this, &Kubrick::veryHardSelected);
0361 
0362     a = actionCollection()->addAction ( QStringLiteral( "new_cube" ));
0363     a->setText (i18n("Make your own..."));
0364     connect (a, &QAction::triggered, game, &Game::newCubeDialog);
0365 
0366     // View menu.
0367     KToggleAction * b;
0368     QActionGroup * viewGroup = new QActionGroup (this);
0369     viewGroup->setExclusive (true);
0370 
0371     b = new KToggleAction   (i18n ("1 Cube"), this);
0372     actionCollection()->addAction ( QStringLiteral( "scene_1" ), b);
0373     b->setToolTip       (i18n ("Show one view of this cube."));
0374     b->setWhatsThis     (i18n ("Show one view of this cube, "
0375                 "from the front."));
0376     b->setIcon          (QIcon::fromTheme( QStringLiteral( "arrow-left" ))); // IDW - Temporary.
0377     connect(b, &KToggleAction::triggered, game, [this] { game->changeScene(OneCube); });
0378     b->setChecked (true);
0379     viewGroup->addAction (b);
0380 
0381     b = new KToggleAction   (i18n ("2 Cubes"), this);
0382     actionCollection()->addAction ( QStringLiteral( "scene_2" ), b);
0383     b->setToolTip       (i18n ("Show two views of this cube."));
0384     b->setWhatsThis     (i18n ("Show two views of this cube, from "
0385                 "the front and the back.  Both can rotate."));
0386     b->setIcon          (QIcon::fromTheme( QStringLiteral( "arrow-up" ))); // IDW - Temporary.
0387     connect(b, &KToggleAction::triggered, game, [this] { game->changeScene(TwoCubes); });
0388     viewGroup->addAction (b);
0389 
0390     b = new KToggleAction   (i18n ("3 Cubes"), this);
0391     actionCollection()->addAction ( QStringLiteral( "scene_3" ), b);
0392     b->setToolTip       (i18n ("Show three views of this cube."));
0393     b->setWhatsThis     (i18n ("Show three views of this cube, a "
0394                 "large one, from the front, and two small "
0395                 "ones, from the front and the back.  Only "
0396                 "the large one can rotate."));
0397     b->setIcon           (QIcon::fromTheme( QStringLiteral( "arrow-right" ))); // IDW - Temporary.
0398     connect(b, &KToggleAction::triggered, game, [this] { game->changeScene(ThreeCubes); });
0399     viewGroup->addAction (b);
0400 
0401     // Demos menu.  See the code after "setupGUI ();".
0402 
0403     // Settings menu.
0404     b = new KToggleAction (i18n("&Watch Shuffling"), this);
0405     actionCollection()->addAction ( QStringLiteral( "watch_shuffling" ), b);
0406     KActionCollection::setDefaultShortcut(b, Qt::Key_W);
0407     connect(b, &KToggleAction::triggered, game, &Game::watchShuffling);
0408 
0409     b = new KToggleAction (i18n("Watch Your &Own Moves"), this);
0410     actionCollection()->addAction ( QStringLiteral( "watch_moves" ), b);
0411     KActionCollection::setDefaultShortcut(b, Qt::Key_O);
0412     connect(b, &KToggleAction::triggered, game, &Game::watchMoves);
0413 
0414     // DISCONTINUED a = actionCollection()->addAction ( QLatin1String( "enable_messages" ));
0415     // a->setText (i18n("Show Beginners' &Messages"));
0416     // connect (a, &QAction::triggered, game, &Game::enableMessages);
0417 
0418     // Sorry to be "non-standard" (below), but I am an English-speaker, born and
0419     // bred, and I just don't find the KDE standard texts "Configure Kubrick"
0420     // and "Configure Shortcuts" to be meaningful in everyday language.  Neither
0421     // would my wife, children and friends, I am sure.
0422 
0423     a = KStandardAction::preferences (game, &Game::optionsDialog, actionCollection());
0424     a->setText (i18n("Kubri&ck Game Settings"));
0425 
0426     // Configure Shortcuts...
0427     a = KStandardAction::keyBindings (this, &Kubrick::optionsConfigureKeys, actionCollection());
0428     a->setText (i18n("Keyboard S&hortcut Settings"));
0429 
0430     /**************************************************************************/
0431     /**************************   KEYSTROKE ACTIONS  **************************/
0432     /**************************************************************************/
0433 
0434     // Keys to choose the axis for a slice move (X, Y or Z).
0435     a = actionCollection()->addAction ( QStringLiteral( "x_axis" ));
0436     a->setText (i18n("X Axis"));
0437     KActionCollection::setDefaultShortcut(a, Qt::Key_X);
0438     connect(a, &QAction::triggered, game, [this] { game->setMoveAxis(0); });
0439 
0440     a = actionCollection()->addAction ( QStringLiteral( "y_axis" ));
0441     a->setText (i18n("Y Axis"));
0442     KActionCollection::setDefaultShortcut(a, Qt::Key_Y);
0443     connect(a, &QAction::triggered, game, [this] { game->setMoveAxis(1); });
0444 
0445     a = actionCollection()->addAction ( QStringLiteral( "z_axis" ));
0446     a->setText (i18n("Z Axis"));
0447     KActionCollection::setDefaultShortcut(a, Qt::Key_Z);
0448     connect(a, &QAction::triggered, game, [this] { game->setMoveAxis(2); });
0449 
0450     // Keys to choose the slice number for a slice move.
0451     const QString ident = QStringLiteral("slice %1");
0452     for (int i = 1; i <= 6; i++) {
0453         a = actionCollection()->addAction (ident.arg(i));
0454         a->setText (i18n("Slice %1", i));
0455         KActionCollection::setDefaultShortcut(a, Qt::Key_0 + i);
0456         connect(a, &QAction::triggered, game, [this, i] { game->setMoveSlice(i); });
0457     }
0458 
0459     // Key to select a rotation of the whole cube (mapped as "slice 0").
0460     a = actionCollection()->addAction ( QStringLiteral( "turn_cube" ));
0461     a->setText (i18n("Turn whole cube"));
0462     KActionCollection::setDefaultShortcut(a, Qt::Key_C);
0463     connect(a, &QAction::triggered, game, [this] { game->setMoveSlice(0); });
0464 
0465     // Keys to choose the direction for a slice move (clock or anti-clock).
0466     a = actionCollection()->addAction ( QStringLiteral( "anti_clockwise" ));
0467     a->setText (i18n("Anti-clockwise"));
0468     KActionCollection::setDefaultShortcut(a, Qt::Key_Left);
0469     connect (a, &QAction::triggered, game, [this] { game->setMoveDirection(0); });
0470 
0471     a = actionCollection()->addAction ( QStringLiteral( "clockwise" ));
0472     a->setText (i18n("Clockwise"));
0473     KActionCollection::setDefaultShortcut(a, Qt::Key_Right);
0474     connect (a, &QAction::triggered, game, [this] { game->setMoveDirection(1); });
0475 
0476     // Keys for Singmaster (sm) moves.
0477     a = mapAction (QStringLiteral("sm_u"), i18n("Move 'Up' face"),
0478                     Qt::Key_U, SM_UP);
0479     a = mapAction (QStringLiteral("sm_d"), i18n("Move 'Down' face"),
0480                     Qt::Key_D, SM_DOWN);
0481     a = mapAction (QStringLiteral("sm_l"), i18n("Move 'Left' face"),
0482                     Qt::Key_L, SM_LEFT);
0483     a = mapAction (QStringLiteral("sm_r"), i18n("Move 'Right' face"),
0484                     Qt::Key_R, SM_RIGHT);
0485     a = mapAction (QStringLiteral("sm_f"), i18n("Move 'Front' face"),
0486                     Qt::Key_F, SM_FRONT);
0487     a = mapAction (QStringLiteral("sm_b"), i18n("Move 'Back' face"),
0488                     Qt::Key_B, SM_BACK);
0489     a = mapAction (QStringLiteral("sm_anti"), i18n("Anti-clockwise move"),
0490                     Qt::Key_Apostrophe, SM_ANTICLOCKWISE);
0491     a = mapAction (QStringLiteral("sm_plus"), i18n("Singmaster two-slice move"),
0492                     Qt::Key_Plus, SM_2_SLICE);
0493     a = mapAction (QStringLiteral("sm_minus"), i18n("Singmaster anti-slice move"),
0494                     Qt::Key_Minus, SM_ANTISLICE);
0495     a = mapAction (QStringLiteral("sm_dot"), i18n("Move an inner slice"),
0496                     Qt::Key_Period, SM_INNER);
0497     a = mapAction (QStringLiteral("sm_return"), i18n("Complete a Singmaster move"),
0498                     Qt::Key_Return, SM_EXECUTE);
0499     a = mapAction (QStringLiteral("sm_enter"), i18n("Complete a Singmaster move"),
0500                     Qt::Key_Enter, SM_EXECUTE);
0501     a = mapAction (QStringLiteral("sm_space"), i18n("Add space to Singmaster moves"),
0502                     Qt::Key_Space, SM_SPACER);
0503 
0504     // IDW - Key for switching the background (temporary) - FIX IT FOR KDE 4.2.
0505     a = actionCollection()->addAction ( QStringLiteral( "switch_background" ));
0506     a->setText (i18n("Switch Background"));
0507     KActionCollection::setDefaultShortcut(a, Qt::Key_K);
0508     connect (a, &QAction::triggered, game, &Game::switchBackground);
0509 }
0510 
0511 
0512 QAction * Kubrick::mapAction (const QString & name,
0513         const QString & text, const Qt::Key key, SingmasterMove mapping)
0514 {
0515     QAction * a;
0516     a = actionCollection()->addAction (name);
0517     a->setText (text);
0518     KActionCollection::setDefaultShortcut(a, key);
0519     connect (a, &QAction::triggered, game, [this, mapping] { game->smInput(mapping); });
0520     return a;
0521 }
0522 
0523 
0524 void Kubrick::setToggle (const QString &actionName, bool onOff)
0525 {
0526     ((KToggleAction *) actionCollection()->action (actionName))->setChecked (onOff);
0527 }
0528 
0529 
0530 void Kubrick::setAvail (const QString &actionName, bool onOff)
0531 {
0532     ((QAction *) actionCollection()->action (actionName))->setEnabled (onOff);
0533 }
0534 
0535 
0536 void Kubrick::setSingmaster (const QString & smString)
0537 {
0538     singmasterMoves->setText (smString);
0539 }
0540 
0541 
0542 void Kubrick::setSingmasterSelection (const int start, const int length)
0543 {
0544     singmasterMoves->setSelection (start, length);
0545 }
0546 
0547 
0548 int Kubrick::fillPuzzleList (KSelectAction * s, const PuzzleItem itemList [])
0549 {
0550     QStringList list;
0551 
0552     for (uint i=0; (!itemList[i].menuText.isEmpty()); i++) {
0553     list.append (KLocalizedString(itemList[i].menuText).toString());
0554     }
0555     s->setItems(list);
0556     return (list.count() - 1);
0557 }
0558 
0559 
0560 void Kubrick::fillDemoList (const DemoItem itemList [], QList<QAction *> & list,
0561                 const QString &uilist, void(Kubrick::*slot)())
0562 {
0563     // Generate an action list with one action for each item in the demo list.
0564     for (uint i = 0; itemList[i].filename != QLatin1String("END"); i++) {
0565     QAction * t = new QAction (KLocalizedString(itemList[i].menuText).toString(), this);
0566     actionCollection()->addAction ( QStringLiteral("%1%2" ).arg(uilist).arg(i), t);
0567     t->setData (i);     // Save the index of the item inside the action.
0568     list.append (t);
0569     connect (t, &QAction::triggered, this, slot);
0570     }
0571 
0572     // Plug the action list into the Demos menu.
0573     plugActionList (uilist, list);
0574 }
0575 
0576 
0577 void Kubrick::saveNewToolbarConfig()
0578 {
0579     // This destroys our actions lists ...
0580     KXmlGuiWindow::saveNewToolbarConfig();
0581 
0582     // ... so plug them again
0583     plugActionList (QStringLiteral("patterns_list"), patternList);
0584     plugActionList (QStringLiteral("demo_moves_list"), movesList);
0585 }
0586 
0587 
0588 void Kubrick::easySelected (int index)
0589 {
0590     statusBarLabel->setText (KLocalizedString(easyItems [index].menuText).toString());
0591     game->changePuzzle (easyItems [index]);
0592 }
0593 
0594 
0595 void Kubrick::notSoEasySelected (int index)
0596 {
0597     statusBarLabel->setText (KLocalizedString(notSoEasyItems [index].menuText).toString());
0598     game->changePuzzle (notSoEasyItems [index]);
0599 }
0600 
0601 
0602 void Kubrick::hardSelected (int index)
0603 {
0604     statusBarLabel->setText (KLocalizedString (hardItems [index].menuText).toString());
0605     game->changePuzzle (hardItems [index]);
0606 }
0607 
0608 
0609 void Kubrick::veryHardSelected (int index)
0610 {
0611     statusBarLabel->setText (KLocalizedString (veryHardItems [index].menuText).toString());
0612     game->changePuzzle (veryHardItems [index]);
0613 }
0614 
0615 
0616 void Kubrick::patternSelected()
0617 {
0618     // Retrieve the index of the demo item from the action.
0619     const QAction * action = static_cast <const QAction *> (sender());
0620     int index = action->data().toInt();
0621 
0622     if (index > 0) {
0623     game->loadDemo (patterns[index].filename);
0624         statusBarLabel->setText (KLocalizedString(patterns[index].menuText).toString());
0625     }
0626     else {
0627     KMessageBox::information (this,
0628         KLocalizedString (patternMovesInfo).toString(),
0629         i18n ("Pretty Patterns"));
0630     }
0631 }
0632 
0633 
0634 void Kubrick::movesSelected()
0635 {
0636     // Retrieve the index of the demo item from the action.
0637     const QAction * action = static_cast <const QAction *> (sender());
0638     int index = action->data().toInt();
0639 
0640     if (index > 0) {
0641     game->loadDemo (solvingMoves[index].filename);
0642         statusBarLabel->setText (KLocalizedString(solvingMoves[index].menuText).toString());
0643     }
0644     else {
0645     KMessageBox::information (this,
0646         KLocalizedString (solvingMovesInfo).toString(),
0647         i18n ("Solution Moves"));
0648     }
0649 }
0650 
0651 
0652 void Kubrick::describePuzzle (int xDim, int yDim, int zDim, int shMoves)
0653 {
0654     QString descr;
0655     if ((xDim == yDim) && (yDim == zDim)) {
0656     descr = i18n ("%1x%2x%3 cube, %4 shuffling moves",
0657             xDim, yDim, zDim, shMoves);
0658     }
0659     else if ((xDim != 1) && (yDim != 1) && (zDim != 1)) {
0660     descr = i18n ("%1x%2x%3 brick, %4 shuffling moves",
0661             xDim, yDim, zDim, shMoves);
0662     }
0663     else {
0664     descr = i18n ("%1x%2x%3 mat, %4 shuffling moves",
0665             xDim, yDim, zDim, shMoves);
0666     }
0667     statusBarLabel->setText (descr);
0668 }
0669 
0670 
0671 void Kubrick::optionsConfigureKeys()
0672 {
0673     KShortcutsDialog::showDialog(actionCollection(), KShortcutsEditor::LetterShortcutsAllowed, this);
0674 }
0675 
0676 
0677 // This method is invoked by KXmlGuiWindow when the window is closed, whether by
0678 // selecting the "Quit" action or by clicking the "X" widget at the top right.
0679 
0680 bool Kubrick::queryClose ()
0681 {
0682     game->saveState ();             // Save the current state of the cube.
0683     return (true);
0684 }
0685 
0686