1 /*****************************************************************************
2  * LibreMines                                                                *
3  * Copyright (C) 2020-2021  Bruno Bollos Correa                              *
4  *                                                                           *
5  * This program is free software: you can redistribute it and/or modify      *
6  * it under the terms of the GNU General Public License as published by      *
7  * the Free Software Foundation, either version 3 of the License, or         *
8  * (at your option) any later version.                                       *
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 General Public License for more details.                              *
14  *                                                                           *
15  * You should have received a copy of the GNU General Public License         *
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.     *
17  *****************************************************************************
18  */
19 
20 
21 #include <QRandomGenerator>
22 #include <QDebug>
23 #include <QFont>
24 #include <QMouseEvent>
25 #include <QStyleFactory>
26 #include <QApplication>
27 #include <QKeyEvent>
28 #include <QScreen>
29 #include <QDir>
30 #include <QLineEdit>
31 #include <QAction>
32 #include <QMenuBar>
33 #include <QStatusBar>
34 #include <QShortcut>
35 #include <QMessageBox>
36 #include <QTranslator>
37 #include <QStandardPaths>
38 
39 #include "libreminesgui.h"
40 #include "libreminesscoresdialog.h"
41 #include "libreminesconfig.h"
42 #include "libreminesviewscoresdialog.h"
43 
CellGui()44 LibreMinesGui::CellGui::CellGui():
45     button(nullptr),
46     label(nullptr)
47 {
48 
49 }
50 
LibreMinesGui(QWidget * parent,const int thatWidth,const int thatHeight)51 LibreMinesGui::LibreMinesGui(QWidget *parent, const int thatWidth, const int thatHeight) :
52     QMainWindow(parent),
53     gameEngine(),
54     principalMatrix( std::vector< std::vector<CellGui> >(0) ),
55     iLimitHeightField( 0 ),
56     iLimitWidthField( 0 ),
57     cellLength( 0 ),
58     difficult( NONE ),
59     preferences( new LibreMinesPreferencesDialog(this) ),
60     dirAppData( QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) )
61 {
62     this->resize(800, 600);
63 
64     connect(preferences, &LibreMinesPreferencesDialog::SIGNAL_optionChanged,
65             this, &LibreMinesGui::SLOT_optionChanged);
66 
67     // Unable central widget when the preferences dialog is active
68     // Also update the preferences when finished
69     connect(preferences, &LibreMinesPreferencesDialog::SIGNAL_visibilityChanged,
70             [this](const bool visible)
71     {
72         this->centralWidget()->setEnabled(!visible);
73         this->vUpdatePreferences();
74     });
75 
76     // Quit the application with Ctrl + Q
77     connect(new QShortcut(QKeySequence(Qt::CTRL | Qt::Key_Q), this), &QShortcut::activated,
78             this, &LibreMinesGui::SLOT_quitApplication);
79 
80     // Create interface with the passed dimensions
81     vCreateGUI(thatWidth, thatHeight);
82     vShowMainMenu();
83 
84     qApp->installEventFilter(this);
85 
86     this->setWindowIcon(QIcon(":/icons_rsc/icons/libremines.svg"));
87     this->setWindowTitle("LibreMines");
88 
89     // Initializr keyboard controller attributes
90     controller.ctrlPressed = false;
91     controller.active = false;
92     controller.currentX = 0;
93     controller.currentY = 0;
94 
95     // Load configuration file and set the theme
96     vLastSessionLoadConfigurationFile();
97     fieldTheme.vSetMinefieldTheme(preferences->optionMinefieldTheme(), cellLength);
98     vSetFacesReaction(preferences->optionFacesReaction());
99 
100     // Necessary for some reason
101     QTimer::singleShot(100, [this](){ vSetApplicationTheme(preferences->optionApplicationStyle()); });
102 }
103 
~LibreMinesGui()104 LibreMinesGui::~LibreMinesGui()
105 {
106     vResetPrincipalMatrix();
107 
108     vLastSessionSaveConfigurationFile();
109 }
110 
eventFilter(QObject * object,QEvent * event)111 bool LibreMinesGui::eventFilter(QObject* object, QEvent* event)
112 {
113     Q_UNUSED(object)
114 
115     // Just handle those two events
116     if(event->type() != QEvent::KeyPress &&
117        event->type() != QEvent::KeyRelease)
118         return false;
119 
120     // If the game is not running, do not deal woth the event
121     if(!gameEngine || !gameEngine->isGameActive())
122         return false;
123 
124     // If the GUI is not ready
125     if(principalMatrix.empty() ||
126        !principalMatrix[0][0].button ||
127        !principalMatrix[0][0].button->isEnabled())
128         return false;
129 
130     // If the keyboard controller has invalid settings
131     if(!controller.valid)
132         return false;
133 
134 
135     // Lock the cursor on the lower left of the screen while the controller is activated
136 //    if(controller.active)
137 //    {
138 //        qApp->overrideCursor()->setPos(90*qApp->primaryScreen()->geometry().width()/100,
139 //                                       90*qApp->primaryScreen()->geometry().height()/100);
140 //    }
141 
142     switch(event->type())
143     {
144         case QEvent::KeyPress:
145         {
146             Qt::Key key = (Qt::Key)((QKeyEvent*)event)->key();
147 
148             if(key == Qt::Key_Control)
149             {
150                 controller.ctrlPressed = true;
151                 return true;
152             }
153             if(controller.active)
154             {
155                 if(key == controller.keyLeft ||
156                    key == controller.keyUp ||
157                    key == controller.keyDown ||
158                    key == controller.keyRight ||
159                    key == controller.keyCenterCell ||
160                    key == controller.keyFlagCell ||
161                    key == controller.keyReleaseCell)
162                 {
163                     return true;
164                 }
165             }
166 
167         }break;
168 
169         case QEvent::KeyRelease:
170         {
171             Qt::Key key = (Qt::Key)((QKeyEvent*)event)->key();
172 
173             if(key == Qt::Key_Control)
174             {
175                 controller.ctrlPressed = false;
176                 return true;
177             }
178 
179             // Active the controller depending on the key
180             // Additionally hide the cursor when the controller is activated
181             if(!controller.active)
182             {
183                 if(key == controller.keyLeft ||
184                    key == controller.keyUp ||
185                    key == controller.keyDown ||
186                    key == controller.keyRight)
187                 {
188                     controller.active = true;
189                     vKeyboardControllerSetCurrentCell(0, 0);
190                     qApp->setOverrideCursor(QCursor(Qt::BlankCursor));
191                     qApp->overrideCursor()->setPos(90*qApp->primaryScreen()->geometry().width()/100,
192                                                    90*qApp->primaryScreen()->geometry().height()/100);
193 
194                     this->setFocus();
195 
196                     return true;
197                 }
198             }
199             else
200             {
201                 if(key == controller.keyLeft ||
202                    key == controller.keyUp ||
203                    key == controller.keyDown ||
204                    key == controller.keyRight ||
205                    key == controller.keyCenterCell ||
206                    key == controller.keyFlagCell ||
207                    key == controller.keyReleaseCell)
208                 {
209                     this->setFocus();
210                 }
211 
212 
213                 if(key == controller.keyLeft)
214                 {
215                     vKeyboardControllerMoveLeft();
216                     return true;
217                 }
218                 if(key == controller.keyUp)
219                 {
220                     vKeyboardControllerMoveUp();
221                     return true;
222                 }
223                 if(key == controller.keyDown)
224                 {
225                     vKeyboardControllerMoveDown();
226                     return true;
227                 }
228                 if(key == controller.keyRight)
229                 {
230                     vKeyboardControllerMoveRight();
231                     return true;
232                 }
233                 if(key == controller.keyReleaseCell)
234                 {
235                     const LibreMinesGameEngine::CellGameEngine& cell =
236                             gameEngine->getPrincipalMatrix()[controller.currentX][controller.currentY];
237 
238                     if(cell.isHidden)
239                     {
240                         Q_EMIT SIGNAL_cleanCell(controller.currentX, controller.currentY);
241                         return true;
242                     }
243                     if(preferences->optionCleanNeighborCellsWhenClickedOnShowedCell())
244                     {
245                         Q_EMIT SIGNAL_cleanNeighborCells(controller.currentX, controller.currentY);
246                         return true;
247                     }
248 
249                 }
250                 if(key == controller.keyFlagCell)
251                 {
252                     Q_EMIT SIGNAL_addOrRemoveFlag(controller.currentX, controller.currentY);
253                     return true;
254                 }
255                 if(key == Qt::Key_Space)
256                 {
257                     vKeyboardControllerCenterCurrentCell();
258                     return true;
259                 }
260                 if(key == Qt::Key_Escape)
261                 {
262                     controller.active = false;
263                     vKeyboardControllUnsetCurrentCell();
264                     qApp->restoreOverrideCursor();
265                     return true;
266                 }
267             }
268 
269         }break;
270 
271         default:
272             break;
273     }
274 
275     return false;
276 }
277 
resizeEvent(QResizeEvent * e)278 void LibreMinesGui::resizeEvent(QResizeEvent *e)
279 {
280     // If we just call the function here, the application freezes
281     QTimer::singleShot(1, [this](){ vAdjustMainMenu(); vAdjustInterfaceInGame(); });
282     QWidget::resizeEvent(e);
283 }
284 
closeEvent(QCloseEvent * e)285 void LibreMinesGui::closeEvent(QCloseEvent *e)
286 {
287     SLOT_quitApplication();
288     e->ignore();
289 }
290 
vNewGame(const uchar _X,const uchar _Y,ushort i_nMines_)291 void LibreMinesGui::vNewGame(const uchar _X,
292                              const uchar _Y,
293                              ushort i_nMines_)
294 {
295     vAdjustInterfaceInGame();
296     vShowInterfaceInGame();
297 
298 
299     // Reset the controller attributes
300     controller.ctrlPressed = false;
301     controller.active = false;
302     controller.currentX = 0;
303     controller.currentY = 0;
304     qApp->restoreOverrideCursor();
305 
306     if(!controller.valid)
307     {
308         QMessageBox::warning(this, tr("Keyboard Controller is invalid"),
309                              tr("Dear user, unfortunately your Keyboard Controller preferences"
310                              " are invalid. Therefore you will not be able to play with the keyboard.\n"
311                              "To fix it go to (Main Meun > Options > Preferences) and edit your preferences."));
312     }
313 
314     // Create a new matrix
315     principalMatrix = std::vector<std::vector<CellGui>> (_X, std::vector<CellGui>(_Y));
316 
317     buttonQuitInGame->setEnabled(false);
318     buttonRestartInGame->setEnabled(false);
319     buttonSaveMinefieldAsImage->setEnabled(false);
320     buttonSaveScore->hide();
321 
322     // Create the game engine instance
323     gameEngine.reset(new LibreMinesGameEngine());
324 
325     gameEngine->setFirstCellClean(preferences->optionFirstCellClean());
326     gameEngine->vNewGame(_X, _Y, i_nMines_);
327 
328     // Set the length of each cell
329     if(iLimitWidthField/_X < iLimitHeightField/_Y)
330         cellLength = iLimitWidthField/_X;
331     else
332         cellLength = iLimitHeightField/_Y;
333 
334     if(cellLength < preferences->optionMinimumCellLength())
335         cellLength = preferences->optionMinimumCellLength();
336     else if(cellLength > preferences->optionMaximumCellLength())
337         cellLength = preferences->optionMaximumCellLength();
338 
339 
340     // Update the pixmaps
341     fieldTheme.vSetMinefieldTheme(preferences->optionMinefieldTheme(), cellLength);
342     // Update faces reaction
343     vSetFacesReaction(preferences->optionFacesReaction());
344 
345     widgetBoardContents->setGeometry(0, 0, _X*cellLength, _Y*cellLength);
346 
347     labelFaceReactionInGame->setPixmap(*pmSleepingFace);
348 
349 
350     const bool bCleanNeighborCellsWhenClickedOnShowedLabel =
351             preferences->optionCleanNeighborCellsWhenClickedOnShowedCell();
352 
353     qApp->processEvents();
354 
355     // Create each cell
356     for(uchar j=0; j<_Y; j++)
357     {
358         for (uchar i=0; i<_X; i++)
359         {
360             CellGui& cell = principalMatrix[i][j];
361 
362 //            cell.label = new QLabel_adapted(this);
363 //            cell.button = new QPushButton_adapted(this);
364 
365             cell.label = new QLabel_adapted(widgetBoardContents);
366             cell.button = new QPushButton_adapted(widgetBoardContents);
367 
368             layoutBoard->addWidget(cell.label, j, i);
369             layoutBoard->addWidget(cell.button, j, i);
370 
371             cell.label->resize(cellLength, cellLength);
372             cell.label->setPixmap(fieldTheme.getPixmapFromCellState(ZERO));
373             cell.label->show();
374 
375             cell.button->resize(cellLength, cellLength);
376             cell.button->setIcon(QIcon(fieldTheme.getPixmapButton(false)));
377             cell.button->setIconSize(QSize(cellLength, cellLength));
378             cell.button->show();
379             cell.button->setEnabled(false);
380 
381             connect(cell.button, &QPushButton_adapted::SIGNAL_released,
382                     this, &LibreMinesGui::SLOT_OnCellButtonReleased);
383             connect(cell.button, &QPushButton_adapted::SIGNAL_clicked,
384                     this, &LibreMinesGui::SLOT_OnCellButtonClicked);
385 
386             if(bCleanNeighborCellsWhenClickedOnShowedLabel)
387             {
388                 connect(cell.label, &QLabel_adapted::SIGNAL_released,
389                         this, &LibreMinesGui::SLOT_onCellLabelReleased);
390                 connect(cell.label, &QLabel_adapted::SIGNAL_clicked,
391                         this, &LibreMinesGui::SLOT_onCellLabelClicked);
392             }
393 
394             if(preferences->optionMinefieldGenerationAnimation() == LibreMines::AnimationOn ||
395                (preferences->optionMinefieldGenerationAnimation() == LibreMines::AnimationLimited &&
396                                    i == _X - 1))
397             {
398                 qApp->processEvents();
399             }
400         }
401     }
402 
403     labelTimerInGame->setNum(0);
404     labelYouWonYouLost->setText(" ");
405 
406     buttonQuitInGame->setEnabled(true);
407     buttonRestartInGame->setEnabled(true);
408     buttonSaveMinefieldAsImage->setEnabled(true);
409 
410     // Set the correct state of each cell
411     vAttributeAllCells();
412 
413     // Communication (GameEngine -> GUI)
414     connect(gameEngine.get(), &LibreMinesGameEngine::SIGNAL_showCell,
415             this, &LibreMinesGui::SLOT_showCell);
416     connect(gameEngine.get(), &LibreMinesGameEngine::SIGNAL_endGameScore,
417             this, &LibreMinesGui::SLOT_endGameScore);
418     connect(gameEngine.get(), &LibreMinesGameEngine::SIGNAL_currentTime,
419             this, &LibreMinesGui::SLOT_currentTime);
420     connect(gameEngine.get(), &LibreMinesGameEngine::SIGNAL_minesLeft,
421             this, &LibreMinesGui::SLOT_minesLeft);
422     connect(gameEngine.get(), &LibreMinesGameEngine::SIGNAL_flagCell,
423             this, &LibreMinesGui::SLOT_flagCell);
424     connect(gameEngine.get(), &LibreMinesGameEngine::SIGNAL_unflagCell,
425             this, &LibreMinesGui::SLOT_unflagCell);
426     connect(gameEngine.get(), &LibreMinesGameEngine::SIGNAL_gameWon,
427             this, &LibreMinesGui::SLOT_gameWon);
428     connect(gameEngine.get(), &LibreMinesGameEngine::SIGNAL_gameLost,
429             this, &LibreMinesGui::SLOT_gameLost);
430     connect(gameEngine.get(), &LibreMinesGameEngine::SIGNAL_remakeGame,
431             this, &LibreMinesGui::SLOT_remakeGame);
432 
433     // Communication (GUI -> GameEngine)
434     connect(this, &LibreMinesGui::SIGNAL_cleanCell,
435             gameEngine.get(), &LibreMinesGameEngine::SLOT_cleanCell);
436     if(bCleanNeighborCellsWhenClickedOnShowedLabel)
437     {
438         connect(this, &LibreMinesGui::SIGNAL_cleanNeighborCells,
439                 gameEngine.get(), &LibreMinesGameEngine::SLOT_cleanNeighborCells);
440     }
441     connect(this, &LibreMinesGui::SIGNAL_addOrRemoveFlag,
442             gameEngine.get(), &LibreMinesGameEngine::SLOT_addOrRemoveFlag);
443     connect(this, &LibreMinesGui::SIGNAL_stopGame,
444             gameEngine.get(), &LibreMinesGameEngine::SLOT_stop);
445 
446     connect(buttonSaveScore, &QPushButton::released,
447             gameEngine.get(), &LibreMinesGameEngine::SLOT_generateEndGameScoreAgain);
448 
449 
450     if(preferences->optionProgressBar())
451     {
452         progressBarGameCompleteInGame->setRange(-gameEngine->cellsToUnlock(), 0);
453         progressBarGameCompleteInGame->setValue(-gameEngine->cellsToUnlock());
454 
455         connect(gameEngine.get(), &LibreMinesGameEngine::SIGNAL_showCell,
456                 [this](){ progressBarGameCompleteInGame->setValue(-gameEngine->hiddenCells()); });
457     }
458 
459     // Set the initial value of mines left to the total number
460     //  of mines
461     SLOT_minesLeft(gameEngine->mines());
462 
463     labelFaceReactionInGame->setPixmap(*pmSmillingFace);
464 }
465 
vAttributeAllCells()466 void LibreMinesGui::vAttributeAllCells()
467 {
468     for(uchar j=0; j<gameEngine->lines(); ++j)
469     {
470         for(uchar i=0; i<gameEngine->rows(); ++i)
471         {
472             CellGui& cell = principalMatrix[i][j];
473 
474             cell.button->setEnabled(true);
475 
476             cell.label->setPixmap
477                     (
478                         fieldTheme.getPixmapFromCellState(
479                             gameEngine->getPrincipalMatrix()[i][j].state
480                             )
481                         );
482 
483         }
484     }
485 }
486 
vResetPrincipalMatrix()487 void LibreMinesGui::vResetPrincipalMatrix()
488 {
489     for(std::vector<CellGui>& i: principalMatrix)
490     {
491         for (CellGui& j: i)
492         {
493             delete j.label;
494             delete j.button;
495         }
496     }
497     principalMatrix.clear();
498 }
499 
vCreateGUI(const int width,const int height)500 void LibreMinesGui::vCreateGUI(const int width, const int height)
501 {
502     // Create the interface
503 
504     setCentralWidget(new QWidget(this));
505 
506     // Actions and Menu Bar
507     actionPreferences = new QAction(this);
508     actionHighScores = new QAction(this);
509     actionToggleFullScreen = new QAction(this);
510     actionAbout = new QAction(this);
511     actionAboutQt = new QAction(this);
512     actionGitHubHomePage = new QAction(this);
513 
514     QMenuBar* menuBarGlobal = new QMenuBar(this);
515 
516     menuBarGlobal->setGeometry(0, 0, this->width(), 100);
517 
518     menuOptions = new QMenu(menuBarGlobal);
519     menuHelp = new QMenu(menuBarGlobal);
520 
521     this->setMenuBar(menuBarGlobal);
522     this->setStatusBar(new QStatusBar(this));
523 
524     menuBarGlobal->addAction(menuOptions->menuAction());
525     menuBarGlobal->addAction(menuHelp->menuAction());
526 
527     menuOptions->addActions({actionPreferences, actionHighScores, actionToggleFullScreen});
528     menuHelp->addActions({actionAbout, actionAboutQt, actionGitHubHomePage});
529 
530     menuOptions->setTitle(tr("Options"));
531     menuHelp->setTitle(tr("Help"));
532 
533     actionPreferences->setText(tr("Preferences..."));
534     actionHighScores->setText(tr("High Scores..."));
535     actionToggleFullScreen->setText(tr("Toggle Full Screen"));
536     actionToggleFullScreen->setShortcut(QKeySequence(Qt::Key_F11));
537 
538     actionAbout->setText(tr("About..."));
539     actionAboutQt->setText(tr("About Qt..."));
540     actionGitHubHomePage->setText(tr("GitHub Homepage..."));
541     // Actions and Menu Bar
542 
543 
544     // Interface In Game
545     this->setFont(QFont("Liberation Sans"));
546     labelFaceReactionInGame = new QLabel(centralWidget());
547     labelTimerInGame = new QLabel(centralWidget());
548     lcd_numberMinesLeft = new QLCDNumber(centralWidget());
549     progressBarGameCompleteInGame = new QProgressBar(centralWidget());
550     buttonRestartInGame = new QPushButton(centralWidget());
551     buttonQuitInGame = new QPushButton(centralWidget());
552     buttonSaveMinefieldAsImage = new QPushButton(centralWidget());
553     buttonSaveScore = new QPushButton(centralWidget());
554     labelYouWonYouLost = new QLabel(centralWidget());
555     labelStatisLastMatch = new QLabel(centralWidget());
556 
557     scrollAreaBoard = new QScrollArea(centralWidget());
558     widgetBoardContents = new QWidget();
559 
560     layoutBoard = new QGridLayout();
561     layoutBoard->setSpacing(0);
562 
563     widgetBoardContents->setLayout(layoutBoard);
564     widgetBoardContents->setFocusPolicy(Qt::NoFocus);
565     scrollAreaBoard->setWidget(widgetBoardContents);
566     scrollAreaBoard->setFocusPolicy(Qt::NoFocus);
567 
568     labelTimerInGame->setFont(QFont("Liberation Sans", 40));
569     labelTimerInGame->setNum(0);
570     lcd_numberMinesLeft->setDecMode();
571     lcd_numberMinesLeft->display(0);
572     progressBarGameCompleteInGame->setTextVisible(false);
573     buttonRestartInGame->setText(tr("Restart"));
574     buttonQuitInGame->setText(tr("Quit"));
575     buttonSaveMinefieldAsImage->setText(tr("Save Minefield as Image"));
576     buttonSaveScore->setText(tr("Save Score"));
577     labelYouWonYouLost->setFont(QFont("Liberation Sans", 15));
578 
579     buttonSaveMinefieldAsImage->setShortcut(QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_P));
580     buttonRestartInGame->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_R));
581 
582     vAdjustInterfaceInGame();
583     vHideInterfaceInGame();
584     // Interface In Game
585 
586 
587     // Create Main Menu Widgets
588     buttonEasy = new QPushButton(centralWidget());
589     buttonEasy->setText(tr("Easy") + "\n\n8x8\n\n" + tr("10 Mines"));
590     buttonEasy->setFont(QFont("Liberation Sans",20));
591 
592     buttonMedium= new QPushButton(centralWidget());
593     buttonMedium->setText(tr("Medium") + "\n\n16x16\n\n" + tr("40 Mines"));
594     buttonMedium->setFont(QFont("Liberation Sans", 20));
595 
596 
597     buttonHard = new QPushButton(centralWidget());
598     buttonHard->setText(tr("Hard") + "\n\n30x16\n\n" + tr("99 Mines"));
599     buttonHard->setFont(QFont("Liberation Sans", 20));
600 
601     buttonCustomizedNewGame = new QPushButton(centralWidget());
602     buttonCustomizedNewGame->setText(tr("Customized Game"));
603     buttonCustomizedNewGame->setFont(QFont("Liberation Sans", 20));
604 
605     sbCustomizedX = new QSpinBox(centralWidget());
606     sbCustomizedX->setMinimum(10);
607     sbCustomizedX->setMaximum(100);
608     sbCustomizedX->setValue(20);
609 
610     sbCustomizedY = new QSpinBox(centralWidget());
611     sbCustomizedY->setMinimum(10);
612     sbCustomizedY->setMaximum(100);
613     sbCustomizedY->setValue(20);
614 
615 
616     sbCustomizedPercentageMines = new QSpinBox(centralWidget());
617     sbCustomizedPercentageMines->setMinimum(0);
618     sbCustomizedPercentageMines->setMaximum(100);
619     sbCustomizedPercentageMines->setValue(20);
620 
621     sbCustomizedNumbersOfMines = new QSpinBox(centralWidget());
622     sbCustomizedNumbersOfMines->setMinimum(0);
623     sbCustomizedNumbersOfMines->setMaximum(sbCustomizedX->value() * sbCustomizedY->value() * sbCustomizedPercentageMines->value() / 100);
624     sbCustomizedNumbersOfMines->setValue(sbCustomizedNumbersOfMines->maximum() * sbCustomizedPercentageMines->value() / 100);
625 
626     cbCustomizedMinesInPercentage = new QCheckBox(centralWidget());
627 
628     labelCustomizedX = new QLabel(centralWidget());
629     labelCustomizedX->setText(tr("Width: "));
630 
631     labelCustomizedY = new QLabel(centralWidget());
632     labelCustomizedY->setText(tr("Height: "));
633 
634     labelCustomizedPercentageMines = new QLabel(centralWidget());
635     labelCustomizedPercentageMines->setText(tr("Percent of Mines: "));
636 
637     labelCustomizedNumbersOfMines = new QLabel(centralWidget());
638     labelCustomizedNumbersOfMines->setText(tr("Number of Mines: "));
639     // Create Main Menu Widgets
640 
641     cbCustomizedMinesInPercentage->setChecked(true);
642 
643     labelCustomizedNumbersOfMines->hide();
644     sbCustomizedNumbersOfMines->hide();
645 
646     connect(buttonEasy, &QPushButton::released,
647             this, &LibreMinesGui::SLOT_Easy);
648 
649     connect(buttonMedium, &QPushButton::released,
650             this, &LibreMinesGui::SLOT_Medium);
651 
652     connect(buttonHard, &QPushButton::released,
653             this, &LibreMinesGui::SLOT_Hard);
654 
655     connect(buttonCustomizedNewGame, &QPushButton::released,
656             this, &LibreMinesGui::SLOT_Customized);
657 
658     connect(buttonRestartInGame, &QPushButton::released,
659             this, &LibreMinesGui::SLOT_RestartGame);
660 
661     connect(buttonQuitInGame, &QPushButton::released,
662             this, &LibreMinesGui::SLOT_QuitGame);
663 
664     connect(buttonSaveMinefieldAsImage, &QPushButton::released,
665             this, &LibreMinesGui::SLOT_saveMinefieldAsImage);
666 
667     connect(actionPreferences, &QAction::triggered,
668             preferences, &QDialog::show);
669 
670     connect(actionHighScores, &QAction::triggered,
671             this, &LibreMinesGui::SLOT_showHighScores);
672 
673     connect(actionToggleFullScreen, &QAction::triggered,
674             this, &LibreMinesGui::SLOT_toggleFullScreen);
675 
676 
677     connect(actionAbout, &QAction::triggered,
678             this, &LibreMinesGui::SLOT_showAboutDialog);
679 
680     connect(actionAboutQt, &QAction::triggered,
681             [this](){ QMessageBox::aboutQt(this, "LibreMines"); });
682 
683     connect(actionGitHubHomePage, &QAction::triggered,
684             [](){ QDesktopServices::openUrl(QUrl("https://github.com/Bollos00/LibreMines")); });
685 
686     connect(cbCustomizedMinesInPercentage, &QCheckBox::stateChanged,
687             [this](const int state)
688     {
689        if(state == Qt::Checked)
690        {
691            sbCustomizedPercentageMines->show();
692            labelCustomizedPercentageMines->show();
693 
694            sbCustomizedNumbersOfMines->hide();
695            labelCustomizedNumbersOfMines->hide();
696        }
697        else
698        {
699            sbCustomizedPercentageMines->hide();
700            labelCustomizedPercentageMines->hide();
701 
702            sbCustomizedNumbersOfMines->show();
703            labelCustomizedNumbersOfMines->show();
704        }
705     });
706 
707     connect(sbCustomizedPercentageMines, QOverload<int>::of(&QSpinBox::valueChanged),
708             [this](const int value)
709     {
710         if(!sbCustomizedPercentageMines->isHidden())
711             sbCustomizedNumbersOfMines->setValue(sbCustomizedX->value() * sbCustomizedY->value() * value / 100);
712     });
713 
714     connect(sbCustomizedNumbersOfMines, QOverload<int>::of(&QSpinBox::valueChanged),
715             [this](const int value)
716     {
717         if(!sbCustomizedNumbersOfMines->isHidden())
718             sbCustomizedPercentageMines->setValue(100 * value / (sbCustomizedX->value() * sbCustomizedY->value()) );
719     });
720 
721     auto updateCustomizedNumberOfMinesMaximum
722     {
723         [this]()
724         {
725             sbCustomizedNumbersOfMines->setMaximum(sbCustomizedX->value() * sbCustomizedY->value());
726             sbCustomizedNumbersOfMines->setValue(sbCustomizedX->value() * sbCustomizedY->value() * sbCustomizedPercentageMines->value() / 100);
727         }
728     };
729 
730     connect(sbCustomizedX, QOverload<int>::of(&QSpinBox::valueChanged),
731             updateCustomizedNumberOfMinesMaximum);
732     connect(sbCustomizedY, QOverload<int>::of(&QSpinBox::valueChanged),
733             updateCustomizedNumberOfMinesMaximum);
734 
735     // Tab order of application
736     setTabOrder(buttonEasy, buttonMedium);
737     setTabOrder(buttonMedium, buttonHard);
738     setTabOrder(buttonHard, buttonCustomizedNewGame);
739     setTabOrder(buttonCustomizedNewGame, sbCustomizedNumbersOfMines);
740     setTabOrder(sbCustomizedNumbersOfMines, sbCustomizedPercentageMines);
741     setTabOrder(sbCustomizedPercentageMines, cbCustomizedMinesInPercentage);
742     setTabOrder(cbCustomizedMinesInPercentage, sbCustomizedX);
743     setTabOrder(sbCustomizedX, sbCustomizedY);
744     setTabOrder(sbCustomizedY, buttonRestartInGame);
745     setTabOrder(buttonRestartInGame, buttonQuitInGame);
746     setTabOrder(buttonQuitInGame, buttonSaveMinefieldAsImage);
747     setTabOrder(buttonSaveMinefieldAsImage, buttonSaveScore);
748 
749     if(width == -1 || height == -1)
750     {
751         this->showNormal();
752         this->showMaximized();
753     }
754     else
755     {
756         resize(width, height);
757     }
758 
759 }
760 
vHideMainMenu()761 void LibreMinesGui::vHideMainMenu()
762 {
763     buttonEasy->hide();
764     buttonMedium->hide();
765     buttonHard->hide();
766 
767     buttonCustomizedNewGame->hide();
768 
769     sbCustomizedX->hide();
770     sbCustomizedY->hide();
771     sbCustomizedPercentageMines->hide();
772     sbCustomizedNumbersOfMines->hide();
773 
774     labelCustomizedX->hide();
775     labelCustomizedY->hide();
776     labelCustomizedPercentageMines->hide();
777     labelCustomizedNumbersOfMines->hide();
778 
779     cbCustomizedMinesInPercentage->hide();
780 
781     // Hide de menu bar too
782     menuBar()->hide();
783 
784 }
785 
vShowMainMenu()786 void LibreMinesGui::vShowMainMenu()
787 {
788     buttonEasy->show();
789     buttonMedium->show();
790     buttonHard->show();
791 
792     buttonCustomizedNewGame->show();
793 
794     sbCustomizedX->show();
795     sbCustomizedY->show();
796 
797     labelCustomizedX->show();
798     labelCustomizedY->show();
799 
800     if(cbCustomizedMinesInPercentage->isChecked())
801     {
802         sbCustomizedPercentageMines->show();
803         labelCustomizedPercentageMines->show();
804     }
805     else
806     {
807         sbCustomizedNumbersOfMines->show();
808         labelCustomizedNumbersOfMines->show();
809     }
810 
811     cbCustomizedMinesInPercentage->show();
812 
813     menuBar()->show();
814 }
815 
vAdjustMainMenu()816 void LibreMinesGui::vAdjustMainMenu()
817 {
818 
819     // Width and Height of the main menu
820     const int w = centralWidget()->width();
821     const int h = centralWidget()->height();
822 
823     buttonEasy->setGeometry(w/20, h/20,
824                             2*w/5, 2*h/5);
825 
826     buttonMedium->setGeometry(buttonEasy->x() + buttonEasy->width() + w/10, h/20,
827                               2*w/5, 2*h/5);
828 
829     buttonHard->setGeometry(w/20, buttonEasy->y() + buttonEasy->height() + h/10,
830                             2*w/5, 2*h/5);
831 
832     buttonCustomizedNewGame->setGeometry(buttonEasy->x() + buttonEasy->width() + w/10,
833                                          buttonEasy->y() + buttonEasy->height() + h/10,
834                                          2*w/5,
835                                          1*h/5);
836 
837     labelCustomizedPercentageMines->setGeometry(buttonCustomizedNewGame->x(),
838                                                 buttonCustomizedNewGame->y() + buttonCustomizedNewGame->height(),
839                                                 buttonCustomizedNewGame->width()/2,
840                                                 buttonCustomizedNewGame->height()/3);
841 
842     labelCustomizedNumbersOfMines->setGeometry(labelCustomizedPercentageMines->geometry());
843 
844     labelCustomizedX->setGeometry(labelCustomizedPercentageMines->x(),
845                                   labelCustomizedPercentageMines->y()+labelCustomizedPercentageMines->height(),
846                                   labelCustomizedPercentageMines->width(),
847                                   labelCustomizedPercentageMines->height());
848 
849     labelCustomizedY->setGeometry(labelCustomizedX->x(),
850                                   labelCustomizedX->y()+labelCustomizedX->height(),
851                                   labelCustomizedX->width(),
852                                   labelCustomizedX->height());
853 
854     sbCustomizedPercentageMines->setGeometry(labelCustomizedPercentageMines->x()+labelCustomizedPercentageMines->width(),
855                                              labelCustomizedPercentageMines->y(),
856                                              9*labelCustomizedPercentageMines->width()/10,
857                                              labelCustomizedPercentageMines->height());
858 
859     sbCustomizedNumbersOfMines->setGeometry(sbCustomizedPercentageMines->geometry());
860 
861     cbCustomizedMinesInPercentage->setGeometry(sbCustomizedNumbersOfMines->x() + sbCustomizedNumbersOfMines->width(),
862                                                sbCustomizedNumbersOfMines->y(),
863                                                labelCustomizedNumbersOfMines->width()/10,
864                                                sbCustomizedNumbersOfMines->height());
865 
866     sbCustomizedX->setGeometry(labelCustomizedX->x()+labelCustomizedX->width(), labelCustomizedX->y(),
867                                labelCustomizedX->width(), labelCustomizedX->height());
868 
869     sbCustomizedY->setGeometry(labelCustomizedY->x()+labelCustomizedY->width(), labelCustomizedY->y(),
870                                labelCustomizedY->width(), labelCustomizedY->height());
871 
872     cbCustomizedMinesInPercentage->setStyleSheet(
873                 "QCheckBox::indicator "
874                 "{"
875                 "    width: " + QString::number(cbCustomizedMinesInPercentage->width()) + "px;"
876                 "    height: " + QString::number(cbCustomizedMinesInPercentage->width()) + "px;"
877                 "}");
878 }
879 
SLOT_Easy()880 void LibreMinesGui::SLOT_Easy()
881 {
882     vHideMainMenu();
883     vNewGame(8, 8, 10);
884 
885     difficult = EASY;
886 }
887 
SLOT_Medium()888 void LibreMinesGui::SLOT_Medium()
889 {
890     vHideMainMenu();
891     vNewGame(16, 16, 40);
892 
893     difficult = MEDIUM;
894 
895 }
896 
SLOT_Hard()897 void LibreMinesGui::SLOT_Hard()
898 {
899     vHideMainMenu();
900     vNewGame(30, 16, 99);
901 
902     difficult = HARD;
903 
904 }
905 
SLOT_Customized()906 void LibreMinesGui::SLOT_Customized()
907 {
908     vHideMainMenu();
909     vNewGame(sbCustomizedX->value(), sbCustomizedY->value(), sbCustomizedNumbersOfMines->value());
910 
911     difficult = CUSTOMIZED;
912 
913 }
914 
vAdjustInterfaceInGame()915 void LibreMinesGui::vAdjustInterfaceInGame()
916 {
917     const int w = centralWidget()->width();
918     const int h = centralWidget()->height();
919 
920 
921     iLimitWidthField = 8*w/10;
922     iLimitHeightField = h;
923 
924     scrollAreaBoard->setGeometry(0, 0, iLimitWidthField, iLimitHeightField);
925 
926     labelFaceReactionInGame->setGeometry(88*w /100, h /20,
927                                          9*w /100, 9*w /100);
928     labelTimerInGame->setGeometry(85*w /100, labelFaceReactionInGame->y() + labelFaceReactionInGame->height(),
929                                   15*w /100, h /14);
930     lcd_numberMinesLeft->setGeometry(labelTimerInGame->x(), labelTimerInGame->y()+labelTimerInGame->height(),
931                                      labelTimerInGame->width(), h /7);
932     progressBarGameCompleteInGame->setGeometry(lcd_numberMinesLeft->x(), lcd_numberMinesLeft->y()+lcd_numberMinesLeft->height(),
933                                      lcd_numberMinesLeft->width(), h /20);
934     buttonRestartInGame->setGeometry(progressBarGameCompleteInGame->x(), progressBarGameCompleteInGame->y()+progressBarGameCompleteInGame->height(),
935                                      progressBarGameCompleteInGame->width()/2, h /20);
936     buttonQuitInGame->setGeometry(buttonRestartInGame->x()+buttonRestartInGame->width(), buttonRestartInGame->y(),
937                                   buttonRestartInGame->width(), buttonRestartInGame->height());
938     buttonSaveMinefieldAsImage->setGeometry(buttonRestartInGame->x(), buttonRestartInGame->y() + buttonRestartInGame->height()*1.1,
939                                             progressBarGameCompleteInGame->width(), progressBarGameCompleteInGame->height());
940     buttonSaveScore->setGeometry(buttonSaveMinefieldAsImage->x(), buttonSaveMinefieldAsImage->y() + buttonSaveMinefieldAsImage->height()*1.1,
941                                  buttonSaveMinefieldAsImage->width(), buttonSaveMinefieldAsImage->height());
942     labelYouWonYouLost->setGeometry(buttonSaveScore->x(), buttonSaveScore->y()+buttonSaveScore->height()*1.1,
943                                     lcd_numberMinesLeft->width(), lcd_numberMinesLeft->height());
944     labelStatisLastMatch->setGeometry(labelYouWonYouLost->x(), labelYouWonYouLost->y() + labelYouWonYouLost->height(),
945                                       labelYouWonYouLost->width(), h /5);
946 }
947 
948 
vHideInterfaceInGame()949 void LibreMinesGui::vHideInterfaceInGame()
950 {
951     labelFaceReactionInGame->hide();
952     labelTimerInGame->hide();
953     lcd_numberMinesLeft->hide();
954     progressBarGameCompleteInGame->hide();
955     buttonRestartInGame->hide();
956     buttonQuitInGame->hide();
957     buttonSaveMinefieldAsImage->hide();
958     buttonSaveScore->hide();
959     labelYouWonYouLost->hide();
960     labelStatisLastMatch->hide();
961     widgetBoardContents->hide();
962     scrollAreaBoard->hide();
963 }
964 
vShowInterfaceInGame()965 void LibreMinesGui::vShowInterfaceInGame()
966 {
967     labelFaceReactionInGame->show();
968     labelTimerInGame->show();
969     lcd_numberMinesLeft->show();
970     if(preferences->optionProgressBar())
971         progressBarGameCompleteInGame->show();
972     buttonRestartInGame->show();
973     buttonQuitInGame->show();
974     buttonSaveMinefieldAsImage->show();
975     labelYouWonYouLost->show();
976     labelStatisLastMatch->show();
977     widgetBoardContents->show();
978     scrollAreaBoard->show();
979 }
980 
SLOT_RestartGame()981 void LibreMinesGui::SLOT_RestartGame()
982 {
983     // If the Interface in Game is hidden or not enable, return
984     if(!buttonRestartInGame->isVisible() || !buttonRestartInGame->isEnabled())
985         return;
986 
987     // Check if there is a game happening. If there is one, create a dialog asking
988     //  if the user want to quit the game
989     if(gameEngine && gameEngine->isGameActive() &&
990        progressBarGameCompleteInGame->value() != progressBarGameCompleteInGame->minimum())
991     {
992         QMessageBox messageBox;
993 
994         messageBox.setText(tr("There is a game happening."));
995         messageBox.setInformativeText(tr("Are you sure you want to quit?"));
996         messageBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
997         messageBox.setDefaultButton(QMessageBox::No);
998         int result = messageBox.exec();
999 
1000         if(result == QMessageBox::No)
1001             return;
1002     }
1003 
1004     vResetPrincipalMatrix();
1005 
1006     labelStatisLastMatch->setText(" ");
1007     labelYouWonYouLost->setText(" ");
1008 
1009     const uchar x = gameEngine->rows();
1010     const uchar y = gameEngine->lines();
1011     const ushort mines = gameEngine->mines();
1012 
1013     Q_EMIT SIGNAL_stopGame();
1014     vNewGame(x, y, mines);
1015 }
1016 
SLOT_QuitGame()1017 void LibreMinesGui::SLOT_QuitGame()
1018 {
1019     // Check if there is a game happening. If there is one, create a dialog asking
1020     //  if the user want to quit the game
1021     if(gameEngine && gameEngine->isGameActive() &&
1022        progressBarGameCompleteInGame->value() != progressBarGameCompleteInGame->minimum())
1023     {
1024         QMessageBox messageBox;
1025 
1026         messageBox.setText(tr("There is a game happening."));
1027         messageBox.setInformativeText(tr("Are you sure you want to quit?"));
1028         messageBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
1029         messageBox.setDefaultButton(QMessageBox::No);
1030         int result = messageBox.exec();
1031 
1032         if(result == QMessageBox::No)
1033             return;
1034     }
1035 
1036     qApp->restoreOverrideCursor();
1037 
1038     labelStatisLastMatch->setText(" ");
1039 
1040     Q_EMIT SIGNAL_stopGame();
1041 
1042     vResetPrincipalMatrix();
1043     vHideInterfaceInGame();
1044     vShowMainMenu();
1045 
1046     difficult = NONE;
1047 
1048     gameEngine.reset();
1049 }
1050 
SLOT_OnCellButtonReleased(const QMouseEvent * const e)1051 void LibreMinesGui::SLOT_OnCellButtonReleased(const QMouseEvent *const e)
1052 {
1053     if(!gameEngine->isGameActive() || controller.active)
1054         return;
1055 
1056     labelFaceReactionInGame->setPixmap(*pmSmillingFace);
1057 
1058     // if the button is released outside its area no not treat the event
1059     if(e->localPos().x() >= cellLength || e->localPos().x() < 0 ||
1060        e->localPos().y() >= cellLength || e->localPos().y() < 0)
1061     {
1062         return;
1063     }
1064 
1065     QPushButton_adapted *buttonClicked = (QPushButton_adapted *) sender();
1066 
1067     for(uchar j=0; j<gameEngine->lines(); j++)
1068     {
1069         for (uchar i=0; i<gameEngine->rows(); i++)
1070         {
1071             // Find the emissor of the signal
1072             if(buttonClicked == principalMatrix[i][j].button)
1073             {
1074                 switch (e->button())
1075                 {
1076                     case Qt::RightButton:
1077                         Q_EMIT SIGNAL_addOrRemoveFlag(i, j);
1078                         return;
1079 
1080                     case Qt::LeftButton:
1081                         Q_EMIT SIGNAL_cleanCell(i, j);
1082                         return;
1083 
1084                     default:
1085                         return;
1086                 }
1087             }
1088         }
1089     }
1090 }
1091 
SLOT_OnCellButtonClicked(const QMouseEvent * const e)1092 void LibreMinesGui::SLOT_OnCellButtonClicked(const QMouseEvent *const e)
1093 {
1094     if(!gameEngine->isGameActive() || controller.active)
1095         return;
1096 
1097     if(e->button() != Qt::LeftButton)
1098         return;
1099 
1100     labelFaceReactionInGame->setPixmap(*pmOpenMouthFace);
1101 }
1102 
SLOT_onCellLabelReleased(const QMouseEvent * const e)1103 void LibreMinesGui::SLOT_onCellLabelReleased(const QMouseEvent *const e)
1104 {
1105     if(!gameEngine->isGameActive() || controller.active)
1106         return;
1107 
1108     labelFaceReactionInGame->setPixmap(*pmSmillingFace);
1109 
1110     // if the button is released outside its area no not treat the event
1111     if(e->localPos().x() >= cellLength || e->localPos().x() < 0 ||
1112        e->localPos().y() >= cellLength || e->localPos().y() < 0)
1113     {
1114         return;
1115     }
1116 
1117     QLabel_adapted *buttonClicked = (QLabel_adapted *) sender();
1118 
1119     for(uchar j=0; j<gameEngine->lines(); j++)
1120     {
1121         for (uchar i=0; i<gameEngine->rows(); i++)
1122         {
1123             // Find the emissor of the signal
1124             if(buttonClicked == principalMatrix[i][j].label)
1125             {
1126                 if(e->button() != Qt::LeftButton)
1127                     return;
1128 
1129                 switch (e->button())
1130                 {
1131                     case Qt::LeftButton:
1132                         Q_EMIT SIGNAL_cleanNeighborCells(i, j);
1133                         return;
1134 
1135                     default:
1136                         return;
1137                 }
1138             }
1139         }
1140     }
1141 }
1142 
SLOT_onCellLabelClicked(const QMouseEvent * const e)1143 void LibreMinesGui::SLOT_onCellLabelClicked(const QMouseEvent *const e)
1144 {
1145     Q_UNUSED(e)
1146 
1147     if(!gameEngine->isGameActive() || controller.active)
1148         return;
1149 
1150     labelFaceReactionInGame->setPixmap(*pmGrimacingFace);
1151 
1152 }
1153 
SLOT_showCell(const uchar _X,const uchar _Y)1154 void LibreMinesGui::SLOT_showCell(const uchar _X, const uchar _Y)
1155 {
1156     principalMatrix[_X][_Y].button->hide();
1157 
1158     if(controller.active && controller.currentX == _X && controller.currentY == _Y)
1159     {
1160         vKeyboardControllUnsetCurrentCell();
1161         vKeyboardControllerSetCurrentCell(controller.currentX, controller.currentY);
1162     }
1163 }
1164 
SLOT_endGameScore(LibreMinesScore score,int iCorrectFlags,int iWrongFlags,int iUnlockedCells,double dFlagsPerSecond,double dCellsPerSecond,bool ignorePreferences)1165 void LibreMinesGui::SLOT_endGameScore(LibreMinesScore score,
1166                                       int iCorrectFlags,
1167                                       int iWrongFlags,
1168                                       int iUnlockedCells,
1169                                       double dFlagsPerSecond,
1170                                       double dCellsPerSecond,
1171                                       bool ignorePreferences)
1172 {
1173     QString QS_Statics =
1174             tr("Total time: ") + QString::number(score.iTimeInNs*1e-9, 'f', 3) + tr(" secs") + '\n'
1175             + tr("Correct Flags: ") + QString::number(iCorrectFlags) + '\n'
1176             + tr("Wrong Flags: ") + QString::number(iWrongFlags) + '\n'
1177             + tr("Unlocked Cells: ") + QString::number(iUnlockedCells) + '\n'
1178             + '\n'
1179             + tr("Flags/s: ") + QString::number(dFlagsPerSecond, 'f', 3)  + '\n'
1180             + tr("Cells/s: ") + QString::number(dCellsPerSecond, 'f', 3) + '\n'
1181             + '\n'
1182             + tr("Game Complete: ") + QString::number(score.dPercentageGameCompleted, 'f', 2) + " %";
1183 
1184     labelStatisLastMatch->setText(QS_Statics);
1185 
1186     score.gameDifficulty = difficult;
1187     score.username = preferences->optionUsername();
1188     if(score.username.isEmpty())
1189 #ifdef Q_OS_WINDOWS
1190     {
1191         score.username = qgetenv("USERNAME");
1192     }
1193 #else
1194     {
1195         score.username = qgetenv("USER");
1196     }
1197 #endif
1198 
1199     // Save the score of the current game on the file scoresLibreMines on
1200     //  the "~/.local/share/libremines/" directory. If the file does not
1201     //  exist, a new one will be created.
1202     if(score.dPercentageGameCompleted != 0)
1203     {
1204         QScopedPointer<QFile> fileScores( new QFile(dirAppData.absoluteFilePath("scoresLibreMines")) );
1205 
1206         if(!fileScores->exists())
1207         {
1208             fileScores->open(QIODevice::NewOnly);
1209             fileScores->close();
1210         }
1211 
1212         bool saveScore = false;
1213         // Search for existing scores on the current level
1214         {
1215             fileScores->open(QIODevice::ReadOnly);
1216 
1217             QList<LibreMinesScore> scores;
1218 
1219             QDataStream stream(fileScores.get());
1220             stream.setVersion(QDataStream::Qt_5_12);
1221 
1222             while(!stream.atEnd())
1223             {
1224                 LibreMinesScore s;
1225                 stream >> s;
1226 
1227                 if(s.gameDifficulty == score.gameDifficulty &&
1228                    s.heigth == score.heigth &&
1229                    s.width == score.width &&
1230                    s.mines == score.mines)
1231                 {
1232                     scores.append(s);
1233                 }
1234             }
1235             LibreMinesScore::sort(scores);
1236 
1237             int index = 0;
1238             for(int i=0; i<scores.size(); ++i)
1239             {
1240                 if(LibreMinesScore::bFirstIsBetter(scores.at(i), score))
1241                     index = i+1;
1242                 else
1243                     break;
1244             }
1245 
1246             QString strGameDiffuclty;
1247             if(score.gameDifficulty == EASY)
1248                 strGameDiffuclty = tr("Easy");
1249             else if(score.gameDifficulty == MEDIUM)
1250                 strGameDiffuclty = tr("Medium");
1251             else if(score.gameDifficulty == HARD)
1252                 strGameDiffuclty = tr("Hard");
1253             else if(score.gameDifficulty == CUSTOMIZED)
1254             {
1255                 strGameDiffuclty = tr("Customized ") + QString::number(score.width) +
1256                                    " x " + QString::number(score.heigth) + " : " +
1257                                    QString::number(score.mines);
1258             }
1259 
1260             AskToSaveMatchScore behaviour = preferences->optionAskToSaveMatchScoreBehaviour();
1261 
1262             if(ignorePreferences ||
1263                behaviour == LibreMines::SaveAlways ||
1264                ((behaviour & LibreMines::SaveWhenGameCompleted) && score.bGameCompleted) ||
1265                ((behaviour & LibreMines::SaveWhenNewHighScore) && index == 0))
1266             {
1267                 // open the dialog
1268                 LibreMinesScoresDialog dialog(this, scores.size() + 1);
1269                 dialog.setWindowTitle(strGameDiffuclty);
1270                 dialog.setWindowIcon(QIcon(":/icons_rsc/icons/libremines.svg"));
1271 
1272                 dialog.setScores(scores, &score, index);
1273                 int result = dialog.exec(); Q_UNUSED(result);
1274 
1275                 if(dialog.bSaveEditableScore())
1276                 {
1277                     score.username = dialog.stringUserName();
1278                     saveScore = true;
1279                 }
1280             }
1281         }
1282 
1283         if(saveScore)
1284         {
1285             qDebug() << "Saving score";
1286 
1287             fileScores.reset( new QFile(dirAppData.absoluteFilePath("scoresLibreMines")) );
1288 
1289             if(fileScores->exists())
1290                 fileScores->open(QIODevice::Append);
1291             else
1292                 fileScores->open(QIODevice::WriteOnly);
1293 
1294             QDataStream stream(fileScores.get());
1295 
1296             stream.setVersion(QDataStream::Qt_5_12);
1297 
1298             stream << score;
1299         }
1300 
1301         buttonSaveScore->setVisible(!saveScore);
1302     }
1303 
1304 }
1305 
SLOT_currentTime(const ushort time)1306 void LibreMinesGui::SLOT_currentTime(const ushort time)
1307 {
1308     labelTimerInGame->setNum(time);
1309 }
1310 
SLOT_minesLeft(const ushort minesLeft)1311 void LibreMinesGui::SLOT_minesLeft(const ushort minesLeft)
1312 {
1313     lcd_numberMinesLeft->display(minesLeft);
1314 }
1315 
SLOT_flagCell(const uchar _X,const uchar _Y)1316 void LibreMinesGui::SLOT_flagCell(const uchar _X, const uchar _Y)
1317 {
1318     if(principalMatrix[_X][_Y].button->isHidden())
1319         qDebug(Q_FUNC_INFO);
1320     else
1321     {
1322         principalMatrix[_X][_Y].button->setIcon(QIcon(fieldTheme.getPixmapButton(true)));
1323         principalMatrix[_X][_Y].button->setIconSize(QSize(cellLength, cellLength));
1324     }
1325 
1326     if(controller.active && controller.currentX == _X && controller.currentY == _Y)
1327     {
1328         vKeyboardControllerSetCurrentCell(controller.currentX, controller.currentY);
1329     }
1330 }
1331 
SLOT_unflagCell(const uchar _X,const uchar _Y)1332 void LibreMinesGui::SLOT_unflagCell(const uchar _X, const uchar _Y)
1333 {
1334     if(principalMatrix[_X][_Y].button->isHidden())
1335         qDebug(Q_FUNC_INFO);
1336     else
1337     {
1338         principalMatrix[_X][_Y].button->setIcon(QIcon(fieldTheme.getPixmapButton(false)));
1339         principalMatrix[_X][_Y].button->setIconSize(QSize(cellLength, cellLength));
1340     }
1341 
1342     if(controller.active && controller.currentX == _X && controller.currentY == _Y)
1343     {
1344         vKeyboardControllerSetCurrentCell(controller.currentX, controller.currentY);
1345     }
1346 }
1347 
SLOT_remakeGame()1348 void LibreMinesGui::SLOT_remakeGame()
1349 {
1350     vAttributeAllCells();
1351 }
1352 
SLOT_gameWon()1353 void LibreMinesGui::SLOT_gameWon()
1354 {
1355     switch (difficult)
1356     {
1357         case NONE:
1358             break;
1359         case EASY:
1360             labelYouWonYouLost->setText(tr("You Won") + '\n' + tr("Difficulty: EASY"));
1361             break;
1362         case MEDIUM:
1363             labelYouWonYouLost->setText(tr("You Won") + '\n' + tr("Difficulty: MEDIUM"));
1364             break;
1365         case HARD:
1366             labelYouWonYouLost->setText(tr("You Won") + '\n' + tr("Difficulty: HARD"));
1367             break;
1368         case CUSTOMIZED:
1369             labelYouWonYouLost->setText(tr("You Won") +'\n' + tr("Difficulty: CUSTOM\n") +
1370                                         QString::number(gameEngine->rows()) +
1371                                         "x" + QString::number(gameEngine->lines()) +
1372                                         " : " + QString::number(gameEngine->mines()) + tr(" Mines"));
1373     }
1374 
1375     for(uchar j=0; j<gameEngine->lines(); j++)
1376     {
1377         for (uchar i=0; i<gameEngine->rows(); i++)
1378         {
1379             principalMatrix[i][j].button->setEnabled(!principalMatrix[i][j].button->isHidden());
1380         }
1381     }
1382 
1383     if(controller.active)
1384     {
1385         controller.active = false;
1386         qApp->restoreOverrideCursor();
1387         vKeyboardControllUnsetCurrentCell();
1388     }
1389 
1390     labelFaceReactionInGame->setPixmap(*pmGrinningFace);
1391 }
1392 
SLOT_gameLost(const uchar _X,const uchar _Y)1393 void LibreMinesGui::SLOT_gameLost(const uchar _X, const uchar _Y)
1394 {
1395     switch (difficult)
1396     {
1397         case NONE:
1398             break;
1399         case EASY:
1400             labelYouWonYouLost->setText(tr("You Lost") + '\n' + tr("Difficulty: EASY"));
1401             break;
1402         case MEDIUM:
1403             labelYouWonYouLost->setText(tr("You Lost") + '\n' + tr("Difficulty: MEDIUM"));
1404             break;
1405         case HARD:
1406             labelYouWonYouLost->setText(tr("You Lost") + '\n' + tr("Difficulty: HARD"));
1407             break;
1408         case CUSTOMIZED:
1409             labelYouWonYouLost->setText(tr("You Lost") + '\n' + tr("Difficulty: CUSTOM\n") +
1410                                         QString::number(gameEngine->rows()) +
1411                                         "x" +
1412                                         QString::number(gameEngine->lines()) +
1413                                         " : " +
1414                                         QString::number(gameEngine->mines()) +
1415                                         tr(" Mines"));
1416     }
1417     principalMatrix[_X][_Y].label->setPixmap(fieldTheme.getPixmapBoom());
1418 
1419 
1420     for(uchar j=0; j<gameEngine->lines(); j++)
1421     {
1422         for (uchar i=0; i<gameEngine->rows(); i++)
1423         {
1424             CellGui& cellGui = principalMatrix[i][j];
1425             const LibreMinesGameEngine::CellGameEngine& cellGE = gameEngine->getPrincipalMatrix()[i][j];
1426 
1427 //            cellGui.button->setEnabled(false);
1428 //            cellGui.label->setEnabled(false);
1429 
1430             if(cellGE.isHidden)
1431             {
1432                 if(cellGE.state == MINE &&
1433                    !cellGE.hasFlag)
1434                 {
1435                     cellGui.button->hide();
1436                 }
1437                 else if (cellGE.state != MINE &&
1438                          cellGE.hasFlag)
1439                 {
1440                     cellGui.button->hide();
1441                     cellGui.label->setPixmap(fieldTheme.getPixmapWrongFlag());
1442                 }
1443             }
1444         }
1445     }
1446 
1447     if(controller.active)
1448     {
1449         controller.active = false;
1450         qApp->restoreOverrideCursor();
1451         vKeyboardControllUnsetCurrentCell();
1452     }
1453 
1454     labelFaceReactionInGame->setPixmap(*pmDizzyFace);
1455 }
1456 
SLOT_optionChanged(const QString & name,const QString & value)1457 void LibreMinesGui::SLOT_optionChanged(const QString &name, const QString &value)
1458 {
1459     if(name.compare("ApplicationTheme", Qt::CaseInsensitive) == 0)
1460     {
1461         vSetApplicationTheme(value);
1462     }
1463     else if(name.compare("MinefieldTheme", Qt::CaseInsensitive) == 0)
1464     {
1465        fieldTheme.vSetMinefieldTheme(value, cellLength);
1466     }
1467 }
1468 
SLOT_quitApplication()1469 void LibreMinesGui::SLOT_quitApplication()
1470 {
1471     if(gameEngine && gameEngine->isGameActive() &&
1472        progressBarGameCompleteInGame->value() != progressBarGameCompleteInGame->minimum())
1473     {
1474         QMessageBox messageBox;
1475 
1476         messageBox.setText(tr("There is a game happening."));
1477         messageBox.setInformativeText(tr("Are you sure you want to quit?"));
1478         messageBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
1479         messageBox.setDefaultButton(QMessageBox::No);
1480         int result = messageBox.exec();
1481 
1482         if(result == QMessageBox::No)
1483             return;
1484     }
1485 
1486     this->deleteLater();
1487 }
1488 
SLOT_showAboutDialog()1489 void LibreMinesGui::SLOT_showAboutDialog()
1490 {
1491     QString text =
1492             "LibreMines " + QString(LIBREMINES_PROJECT_VERSION) + "\n" +
1493             tr("Copyright (C) 2020-2021  Bruno Bollos Correa\n"
1494             "\n"
1495             "This program is free software: you can redistribute it and/or modify"
1496             " it under the terms of the GNU General Public License as published by"
1497             " the Free Software Foundation, either version 3 of the License, or"
1498             " (at your option) any later version.\n"
1499             "\n"
1500             "This program is distributed in the hope that it will be useful,"
1501             " but WITHOUT ANY WARRANTY; without even the implied warranty of"
1502             " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the"
1503             " GNU General Public License for more details.\n"
1504             "\n"
1505             "You should have received a copy of the GNU General Public License"
1506             " along with this program.  If not, see <http://www.gnu.org/licenses/>.\n"
1507             "\n"
1508             "Get the source code of LibreMines on\n") +
1509             "<" + QString(LIBREMINES_PROJECT_HOMEPAGE_URL) + ">";
1510 
1511     QMessageBox::about(this, "LibreMines", text);
1512 }
1513 
SLOT_showHighScores()1514 void LibreMinesGui::SLOT_showHighScores()
1515 {
1516     QScopedPointer<QFile> fileScores( new QFile(dirAppData.absoluteFilePath("scoresLibreMines")) );
1517 
1518     if(!fileScores->exists())
1519     {
1520         fileScores->open(QIODevice::NewOnly);
1521         fileScores->close();
1522     }
1523 
1524     fileScores->open(QIODevice::ReadOnly);
1525 
1526     QList<LibreMinesScore> scores;
1527 
1528     QDataStream stream(fileScores.get());
1529     stream.setVersion(QDataStream::Qt_5_12);
1530 
1531     while(!stream.atEnd())
1532     {
1533         LibreMinesScore s;
1534         stream >> s;
1535 
1536         scores.append(s);
1537     }
1538 
1539     // open the dialog
1540     LibreMinesViewScoresDialog dialog(this);
1541     dialog.setWindowIcon(QIcon(":/icons_rsc/icons/libremines.svg"));
1542     dialog.setScores(scores);
1543     dialog.exec();
1544 }
1545 
SLOT_toggleFullScreen()1546 void LibreMinesGui::SLOT_toggleFullScreen()
1547 {
1548     if(isFullScreen())
1549     {
1550         this->showNormal();
1551         this->showMaximized();
1552     }
1553     else
1554     {
1555         this->showFullScreen();
1556     }
1557 }
1558 
SLOT_saveMinefieldAsImage()1559 void LibreMinesGui::SLOT_saveMinefieldAsImage()
1560 {
1561     if(controller.active)
1562         qApp->restoreOverrideCursor();
1563 
1564     QString picturesDirPAth = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation);
1565     if(picturesDirPAth.isEmpty())
1566         picturesDirPAth = QDir::currentPath();
1567 
1568     QString fileName = "libremines_screeshoot" + QDateTime::currentDateTime().toString(Qt::ISODate).replace(':', '_') + ".png";
1569 
1570     QString fullFileName = QFileDialog::getSaveFileName(
1571                            this,
1572                            tr("Save Minefield as image"),
1573                            picturesDirPAth + '/' + fileName,
1574                            tr("Image (*.bmp *.jpg *png *.jpeg)"));
1575 
1576     if(fullFileName.isEmpty())
1577         return;
1578 
1579     QImage image(widgetBoardContents->size(), QImage::Format_RGBA64);
1580     QPainter painter(&image);
1581     widgetBoardContents->render(&painter);
1582     image.save(fullFileName);
1583 
1584     if(controller.active)
1585         qApp->setOverrideCursor(QCursor(Qt::BlankCursor));
1586 }
1587 
vSetApplicationTheme(const QString & theme)1588 void LibreMinesGui::vSetApplicationTheme(const QString& theme)
1589 {
1590     static bool firstTimeHere = true;
1591 
1592     if(theme.compare("default", Qt::CaseInsensitive) == 0)
1593     {
1594         if(!firstTimeHere)
1595         {
1596             QMessageBox::information(this, "LibreMines",
1597                                      "Please reset the application to apply this change");
1598         }
1599     }
1600     else if(theme.compare("FusionDark", Qt::CaseInsensitive) == 0)
1601     {
1602         qApp->setStyleSheet("");
1603         qApp->setStyle(QStyleFactory::create ("Fusion"));
1604         QPalette palette;
1605         palette.setColor(QPalette::BrightText,      Qt::red);
1606         palette.setColor(QPalette::WindowText,      Qt::white);
1607         palette.setColor(QPalette::ToolTipBase,     Qt::white);
1608         palette.setColor(QPalette::ToolTipText,     Qt::white);
1609         palette.setColor(QPalette::Text,            Qt::white);
1610         palette.setColor(QPalette::ButtonText,      Qt::white);
1611         palette.setColor(QPalette::HighlightedText, Qt::black);
1612         palette.setColor(QPalette::Window,          QColor (53, 53, 53));
1613         palette.setColor(QPalette::Base,            QColor (25, 25, 25));
1614         palette.setColor(QPalette::AlternateBase,   QColor (53, 53, 53));
1615         palette.setColor(QPalette::Button,          QColor (53, 53, 53));
1616         palette.setColor(QPalette::Link,            QColor (42, 130, 218));
1617         palette.setColor(QPalette::Highlight,       QColor (42, 130, 218));
1618 
1619         qApp->setPalette(palette);
1620     }
1621     else if(theme.compare("FusionLight", Qt::CaseInsensitive) == 0)
1622     {
1623         qApp->setStyleSheet("");
1624         qApp->setStyle(QStyleFactory::create ("Fusion"));
1625         QPalette palette;
1626         palette.setColor(QPalette::BrightText,      Qt::cyan);
1627         palette.setColor(QPalette::WindowText,      Qt::black);
1628         palette.setColor(QPalette::ToolTipBase,     Qt::black);
1629         palette.setColor(QPalette::ToolTipText,     Qt::black);
1630         palette.setColor(QPalette::Text,            Qt::black);
1631         palette.setColor(QPalette::ButtonText,      Qt::black);
1632         palette.setColor(QPalette::HighlightedText, Qt::white);
1633         palette.setColor(QPalette::Window,          QColor (202, 202, 202));
1634         palette.setColor(QPalette::Base,            QColor (228, 228, 228));
1635         palette.setColor(QPalette::AlternateBase,   QColor (202, 202, 202));
1636         palette.setColor(QPalette::Button,          QColor (202, 202, 202));
1637         palette.setColor(QPalette::Link,            QColor (213, 125, 37));
1638         palette.setColor(QPalette::Highlight,       QColor (42, 130, 218));
1639 
1640         qApp->setPalette(palette);
1641     }
1642     else if(QStyleFactory::keys().contains(theme))
1643     {
1644         qApp->setStyleSheet("");
1645         qApp->setPalette(QPalette());
1646         qApp->setStyle(QStyleFactory::create(theme));
1647     }
1648     else
1649     {
1650         qApp->setPalette(QPalette());
1651         qApp->setStyle("");
1652 
1653         QString prefix;
1654 
1655         if(theme.compare("ConsoleStyle", Qt::CaseInsensitive) == 0)
1656             prefix = ":/qss/ConsoleStyle.qss";
1657         else if(theme.compare("NeonButtons", Qt::CaseInsensitive) == 0)
1658             prefix = ":/qss/NeonButtons.qss";
1659         else if(theme.compare("QDarkStyle", Qt::CaseInsensitive) == 0)
1660             prefix = ":/qdarkstyle/dark/style.qss";
1661         else if(theme.compare("QDarkStyleLight", Qt::CaseInsensitive) == 0)
1662             prefix = ":/qdarkstyle/light/style.qss";
1663         else if(theme.compare("BreezeDark", Qt::CaseInsensitive) == 0)
1664             prefix = ":/breeze/dark.qss";
1665         else if(theme.compare("BreezeLight", Qt::CaseInsensitive) == 0)
1666             prefix = ":/breeze/light.qss";
1667 
1668 
1669         QFile fileQSS(prefix);
1670 
1671         if (!fileQSS.exists())
1672         {
1673             qWarning() << "Unable to set stylesheet, file not found";
1674         }
1675         else
1676         {
1677             fileQSS.open(QFile::ReadOnly | QFile::Text);
1678             QTextStream ts(&fileQSS);
1679             qApp->setStyleSheet(ts.readAll());
1680         }
1681     }
1682 
1683     firstTimeHere = false;
1684 }
1685 
1686 
vSetFacesReaction(const QString & which)1687 void LibreMinesGui::vSetFacesReaction(const QString &which)
1688 {
1689     if(which.compare("Disable", Qt::CaseInsensitive) == 0)
1690     {
1691         pmDizzyFace.reset( new QPixmap() );
1692         pmGrimacingFace.reset( new QPixmap() );
1693         pmGrinningFace.reset( new QPixmap() );
1694         pmOpenMouthFace.reset( new QPixmap() );
1695         pmSleepingFace.reset( new QPixmap() );
1696         pmSmillingFace.reset( new QPixmap() );
1697     }
1698     else
1699     {
1700         QString prefix = ":/facesreaction/faces_reaction/open-emoji-color/";
1701         if(which.compare("OpenEmojiColored", Qt::CaseInsensitive) == 0)
1702             prefix = ":/facesreaction/faces_reaction/open-emoji-color/";
1703         else if(which.compare("OpenEmojiBlack", Qt::CaseInsensitive) == 0)
1704             prefix = ":/facesreaction/faces_reaction/open-emoji-black/";
1705         else if(which.compare("OpenEmojiWhite", Qt::CaseInsensitive) == 0)
1706             prefix = ":/facesreaction/faces_reaction/open-emoji-white/";
1707         else if(which.compare("TwEmojiColored", Qt::CaseInsensitive) == 0)
1708             prefix = ":/facesreaction/faces_reaction/twemoji-color/";
1709         else
1710         {
1711             qWarning() << "Faces reaction option: \"" << qPrintable(which) << "\" will not be handled";
1712         }
1713 
1714         const int length = labelFaceReactionInGame->width();
1715 
1716         pmDizzyFace.reset( new QPixmap( QIcon(prefix + "dizzy_face.svg").pixmap(length, length) ));
1717         pmGrimacingFace.reset( new QPixmap( QIcon(prefix + "grimacing_face.svg").pixmap(length, length) ));
1718         pmGrinningFace.reset( new QPixmap( QIcon(prefix + "grinning_face.svg").pixmap(length, length) ));
1719         pmOpenMouthFace.reset( new QPixmap( QIcon(prefix + "open_mouth_face.svg").pixmap(length, length) ));
1720         pmSleepingFace.reset( new QPixmap( QIcon(prefix + "sleeping_face.svg").pixmap(length, length) ));
1721         pmSmillingFace.reset( new QPixmap( QIcon(prefix + "smilling_face.svg").pixmap(length, length) ));
1722     }
1723 }
1724 
vKeyboardControllerSetCurrentCell(const uchar x,const uchar y)1725 void LibreMinesGui::vKeyboardControllerSetCurrentCell(const uchar x, const uchar y)
1726 {
1727     controller.currentX = x;
1728     controller.currentY = y;
1729 
1730     const LibreMinesGameEngine::CellGameEngine& cellGE = gameEngine->getPrincipalMatrix()[controller.currentX][controller.currentY];
1731     CellGui& cellGui= principalMatrix[controller.currentX][controller.currentY];
1732 
1733     if(cellGE.isHidden)
1734     {
1735 
1736         QImage img = fieldTheme.getPixmapButton(cellGE.hasFlag).toImage();
1737         img.invertPixels();
1738         cellGui.button->setIcon(QIcon(QPixmap::fromImage(img)));
1739     }
1740     else
1741     {
1742         QImage img = fieldTheme.getPixmapFromCellState(cellGE.state).toImage();
1743         img.invertPixels();
1744 
1745         cellGui.label->setPixmap(QPixmap::fromImage(img));
1746     }
1747 
1748     scrollAreaBoard->ensureVisible(x*cellLength + cellLength/2, y*cellLength + cellLength/2,
1749                                    cellLength/2 + 1, cellLength/2 + 1);
1750 }
1751 
vKeyboardControllUnsetCurrentCell()1752 void LibreMinesGui::vKeyboardControllUnsetCurrentCell()
1753 {
1754     const LibreMinesGameEngine::CellGameEngine& cellGE = gameEngine->getPrincipalMatrix()[controller.currentX][controller.currentY];
1755     CellGui& cellGui= principalMatrix[controller.currentX][controller.currentY];
1756 
1757     if(cellGE.isHidden)
1758     {
1759         cellGui.button->setIcon(QIcon(fieldTheme.getPixmapButton(cellGE.hasFlag)));
1760     }
1761     else
1762     {
1763         cellGui.label->setPixmap(fieldTheme.getPixmapFromCellState(cellGE.state));
1764     }
1765 }
1766 
vKeyboardControllerMoveLeft()1767 void LibreMinesGui::vKeyboardControllerMoveLeft()
1768 {
1769     vKeyboardControllUnsetCurrentCell();
1770 
1771     uchar destX = 0;
1772 
1773     if(controller.ctrlPressed)
1774     {
1775         if(preferences->optionWhenCtrlIsPressed() == LibreMines::Jump3Cells &&
1776            controller.currentX >= 3)
1777         {
1778             destX = controller.currentX - 3;
1779         }
1780         else if(preferences->optionWhenCtrlIsPressed() == LibreMines::Jump5Cells &&
1781             controller.currentX >= 5)
1782         {
1783             destX = controller.currentX - 5;
1784         }
1785         else if(preferences->optionWhenCtrlIsPressed() == LibreMines::Jump10Cells &&
1786                 controller.currentX >= 10)
1787         {
1788             destX = controller.currentX - 10;
1789         }
1790 //        else
1791 //        {
1792 //            destX = 0;
1793 //        }
1794     }
1795     else
1796     {
1797         destX = (controller.currentX == 0) ? gameEngine->rows() - 1 : controller.currentX - 1;
1798     }
1799 
1800     vKeyboardControllerSetCurrentCell(destX, controller.currentY);
1801 }
1802 
vKeyboardControllerMoveRight()1803 void LibreMinesGui::vKeyboardControllerMoveRight()
1804 {
1805     vKeyboardControllUnsetCurrentCell();
1806 
1807     uchar destX = gameEngine->rows() - 1;
1808 
1809     if(controller.ctrlPressed)
1810     {
1811         if(preferences->optionWhenCtrlIsPressed() == LibreMines::Jump3Cells &&
1812             controller.currentX < gameEngine->rows() - 3)
1813         {
1814             destX = controller.currentX + 3;
1815         }
1816         else if(preferences->optionWhenCtrlIsPressed() == LibreMines::Jump5Cells &&
1817             controller.currentX < gameEngine->rows() - 5)
1818         {
1819             destX = controller.currentX + 5;
1820         }
1821         else if(preferences->optionWhenCtrlIsPressed() == LibreMines::Jump10Cells &&
1822                 controller.currentX < gameEngine->rows() - 10)
1823         {
1824             destX = controller.currentX + 10;
1825         }
1826     }
1827     else
1828     {
1829         destX = (controller.currentX == gameEngine->rows() - 1) ? 0 : (controller.currentX + 1);
1830     }
1831 
1832     vKeyboardControllerSetCurrentCell(destX, controller.currentY);
1833 
1834 }
1835 
vKeyboardControllerMoveDown()1836 void LibreMinesGui::vKeyboardControllerMoveDown()
1837 {
1838     vKeyboardControllUnsetCurrentCell();
1839 
1840     uchar destY = gameEngine->lines() - 1;
1841 
1842     if(controller.ctrlPressed)
1843     {
1844         if(preferences->optionWhenCtrlIsPressed() == LibreMines::Jump3Cells &&
1845             controller.currentY < gameEngine->lines() - 3)
1846         {
1847             destY = controller.currentY + 3;
1848         }
1849         else if(preferences->optionWhenCtrlIsPressed() == LibreMines::Jump5Cells &&
1850             controller.currentY < gameEngine->lines() - 5)
1851         {
1852             destY = controller.currentY + 5;
1853         }
1854         else if(preferences->optionWhenCtrlIsPressed() == LibreMines::Jump10Cells &&
1855                 controller.currentY < gameEngine->lines() - 10)
1856         {
1857             destY = controller.currentY + 10;
1858         }
1859     }
1860     else
1861     {
1862         destY = (controller.currentY == gameEngine->lines() - 1) ? 0 : (controller.currentY + 1);
1863     }
1864 
1865     vKeyboardControllerSetCurrentCell(controller.currentX, destY);
1866 
1867 }
1868 
vKeyboardControllerMoveUp()1869 void LibreMinesGui::vKeyboardControllerMoveUp()
1870 {
1871     vKeyboardControllUnsetCurrentCell();
1872 
1873     uchar destY = 0;
1874 
1875     if(controller.ctrlPressed)
1876     {
1877         if(preferences->optionWhenCtrlIsPressed() == LibreMines::Jump3Cells &&
1878             controller.currentY >= 3)
1879         {
1880             destY = controller.currentY - 3;
1881         }
1882         else if(preferences->optionWhenCtrlIsPressed() == LibreMines::Jump5Cells &&
1883             controller.currentY >= 5)
1884         {
1885             destY = controller.currentY - 5;
1886         }
1887         else if(preferences->optionWhenCtrlIsPressed() == LibreMines::Jump10Cells &&
1888                 controller.currentY >= 10)
1889         {
1890             destY = controller.currentY - 10;
1891         }
1892     }
1893     else
1894     {
1895         destY = (controller.currentY == 0) ? gameEngine->lines() - 1 : controller.currentY - 1;
1896     }
1897 
1898     vKeyboardControllerSetCurrentCell(controller.currentX, destY);
1899 }
1900 
vKeyboardControllerCenterCurrentCell()1901 void LibreMinesGui::vKeyboardControllerCenterCurrentCell()
1902 {
1903     const uchar x = controller.currentX;
1904     const uchar y = controller.currentY;
1905     scrollAreaBoard->ensureVisible(x*cellLength + cellLength/2, y*cellLength + cellLength/2,
1906                                    cellLength/2 + scrollAreaBoard->width()/2, cellLength/2 + scrollAreaBoard->height()/2);
1907 
1908 }
1909 
vLastSessionLoadConfigurationFile()1910 void LibreMinesGui::vLastSessionLoadConfigurationFile()
1911 {
1912     QScopedPointer<QFile> fileScores( new QFile(dirAppData.absoluteFilePath("libreminesLastSession.txt")) );
1913 
1914     if(fileScores->open(QIODevice::ReadOnly))
1915     {
1916         QTextStream stream(fileScores.get());
1917 
1918         while(!stream.atEnd())
1919         {
1920             QString s = stream.readLine();
1921 
1922             if(s.at(0) == '#')
1923                 continue;
1924 
1925             QStringList terms = s.split(" ");
1926 
1927             if(terms.size() < 2)
1928                 continue;
1929 
1930             if(terms.at(0).compare("FirstCellClean", Qt::CaseInsensitive) == 0)
1931             {
1932                 if(terms.size() != 2)
1933                     continue;
1934 
1935                 preferences->setOptionFirstCellClean(terms.at(1));
1936             }
1937             else if(terms.at(0).compare("ApplicationStyle", Qt::CaseInsensitive) == 0 ||
1938                     terms.at(0).compare("ApplicationTheme", Qt::CaseInsensitive) == 0)
1939             {
1940                 if(terms.size() != 2)
1941                     continue;
1942 
1943                 preferences->setOptionApplicationStyle(terms.at(1));
1944             }
1945             else if(terms.at(0).compare("ClearNeighborCellsWhenClickedOnShowedCell", Qt::CaseInsensitive) == 0)
1946             {
1947                 if(terms.size() != 2)
1948                     continue;
1949 
1950                 preferences->setOptionCleanNeighborCellsWhenClickedOnShowedCell(terms.at(1));
1951             }
1952             else if(terms.at(0).compare("Username", Qt::CaseInsensitive) == 0)
1953             {
1954                 if(terms.size() != 2)
1955                     continue;
1956 
1957                 preferences->setOptionUsername(terms.at(1));
1958             }
1959             else if(terms.at(0).compare("CustomizedPercentageOfMines", Qt::CaseInsensitive) == 0)
1960             {
1961                 if(terms.size() != 2)
1962                     continue;
1963 
1964                 sbCustomizedPercentageMines->setValue(terms.at(1).toInt());
1965             }
1966             else if(terms.at(0).compare("CustomizedX", Qt::CaseInsensitive) == 0)
1967             {
1968                 if(terms.size() != 2)
1969                     continue;
1970 
1971                 sbCustomizedX->setValue(terms.at(1).toInt());
1972             }
1973             else if(terms.at(0).compare("CustomizedY", Qt::CaseInsensitive) == 0)
1974             {
1975                 if(terms.size() != 2)
1976                     continue;
1977 
1978                 sbCustomizedY->setValue(terms.at(1).toInt());
1979             }
1980             else if(terms.at(0).compare("KeyboardControllerKeys", Qt::CaseInsensitive) == 0)
1981             {
1982                 if(terms.size() == 7)
1983                 {
1984                     preferences->setOptionKeyboardControllerKeys(
1985                                 {
1986                                     terms.at(1).toInt(nullptr, 16),
1987                                     terms.at(2).toInt(nullptr, 16),
1988                                     terms.at(3).toInt(nullptr, 16),
1989                                     terms.at(4).toInt(nullptr, 16),
1990                                     terms.at(5).toInt(nullptr, 16),
1991                                     terms.at(6).toInt(nullptr, 16)
1992                                 });
1993                 }
1994                 else if(terms.size() == 8)
1995                 {
1996                     preferences->setOptionKeyboardControllerKeys(
1997                                 {
1998                                     terms.at(1).toInt(nullptr, 16),
1999                                     terms.at(2).toInt(nullptr, 16),
2000                                     terms.at(3).toInt(nullptr, 16),
2001                                     terms.at(4).toInt(nullptr, 16),
2002                                     terms.at(5).toInt(nullptr, 16),
2003                                     terms.at(6).toInt(nullptr, 16),
2004                                     terms.at(7).toInt(nullptr, 16)
2005                                 });
2006                 }
2007             }
2008             else if(terms.at(0).compare("MinefieldTheme", Qt::CaseInsensitive) == 0)
2009             {
2010                 if(terms.size() != 2)
2011                     continue;
2012 
2013                 preferences->setOptionMinefieldTheme(terms.at(1));
2014             }
2015             else if(terms.at(0).compare("CustomizedMinesInPercentage", Qt::CaseInsensitive) == 0)
2016             {
2017                 if(terms.size() != 2)
2018                     continue;
2019 
2020                 cbCustomizedMinesInPercentage->setChecked(terms.at(1).compare("On", Qt::CaseInsensitive) == 0);
2021             }
2022             else if(terms.at(0).compare("WhenCtrlIsPressed", Qt::CaseInsensitive) == 0)
2023             {
2024                 if(terms.size() != 2)
2025                     continue;
2026 
2027                 preferences->setOptionWhenCtrlIsPressed(terms.at(1).toInt());
2028             }
2029             else if(terms.at(0).compare("MinimumCellLength", Qt::CaseInsensitive) == 0)
2030             {
2031                 if(terms.size() != 2)
2032                     continue;
2033 
2034                 preferences->setOptionMinimumCellLength(terms.at(1).toInt());
2035             }
2036             else if(terms.at(0).compare("MaximumCellLength", Qt::CaseInsensitive) == 0)
2037             {
2038                 if(terms.size() != 2)
2039                     continue;
2040 
2041                 preferences->setOptionMaximumCellLength(terms.at(1).toInt());
2042             }
2043             else if(terms.at(0).compare("FacesReaction", Qt::CaseInsensitive) == 0)
2044             {
2045                 if(terms.size() != 2)
2046                     continue;
2047 
2048                 preferences->setOptionFacesReaction(terms.at(1));
2049             }
2050             else if(terms.at(0).compare("ProgressBar", Qt::CaseInsensitive) == 0)
2051             {
2052                 if(terms.size() != 2)
2053                     continue;
2054 
2055                 preferences->setOptionProgressBar(terms.at(1));
2056             }
2057             else if(terms.at(0).compare("MinefieldGenerationAnimation", Qt::CaseInsensitive) == 0)
2058             {
2059                 if(terms.size() != 2)
2060                     continue;
2061 
2062                 preferences->setOptionMinefieldGenerationAnimation(terms.at(1));
2063             }
2064             else if(terms.at(0).compare("AskToSaveMatchScoreBehaviour", Qt::CaseInsensitive) == 0)
2065             {
2066                 if(terms.size() != 2)
2067                     continue;
2068 
2069                 preferences->setOptionAskToSaveMatchScoreBehaviour(terms.at(1).toUInt());
2070             }
2071 
2072         }
2073     }
2074 
2075 
2076     QScopedPointer<QFile> fileLanguage( new QFile(dirAppData.absoluteFilePath("libreminesDefaultLanguage.txt")) );
2077 
2078     if(fileLanguage->open(QIODevice::ReadOnly))
2079     {
2080         QTextStream stream(fileLanguage.get());
2081         QString language;
2082         stream >> language;
2083 
2084         preferences->setOptionLanguage(language);
2085     }
2086 
2087     vUpdatePreferences();
2088 }
2089 
vLastSessionSaveConfigurationFile()2090 void LibreMinesGui::vLastSessionSaveConfigurationFile()
2091 {
2092     QScopedPointer<QFile> fileLastSession( new QFile(dirAppData.absoluteFilePath("libreminesLastSession.txt")) );
2093 
2094     fileLastSession->open(QIODevice::WriteOnly);
2095     {
2096         QTextStream stream(fileLastSession.get());
2097 
2098         stream << "FirstCellClean" << ' ' << (preferences->optionFirstCellClean() ? "On" : "Off") << '\n'
2099                << "ApplicationStyle" << ' ' << preferences->optionApplicationStyle() << '\n'
2100                << "ClearNeighborCellsWhenClickedOnShowedCell" << ' ' << (preferences->optionCleanNeighborCellsWhenClickedOnShowedCell() ? "On" : "Off") << '\n'
2101                << "Username" << ' ' << preferences->optionUsername() << '\n'
2102                << "CustomizedPercentageOfMines" << ' ' << sbCustomizedPercentageMines->value() << '\n'
2103                << "CustomizedX" << ' ' << sbCustomizedX->value() << '\n'
2104                << "CustomizedY" << ' ' << sbCustomizedY->value() << '\n'
2105                << "KeyboardControllerKeys" << ' ' << preferences->optionKeyboardControllerKeysString() << '\n'
2106                << "MinefieldTheme" << ' ' << preferences->optionMinefieldTheme() << '\n'
2107                << "CustomizedMinesInPercentage" << ' ' << (cbCustomizedMinesInPercentage->isChecked() ? "On" : "Off") << '\n'
2108                << "WhenCtrlIsPressed" << ' ' << preferences->optionWhenCtrlIsPressed() << '\n'
2109                << "MinimumCellLength" << ' ' << preferences->optionMinimumCellLength() << '\n'
2110                << "MaximumCellLength" << ' ' << preferences->optionMaximumCellLength() << '\n'
2111                << "FacesReaction" << ' ' << preferences->optionFacesReaction() << '\n'
2112                << "ProgressBar" << ' ' << (preferences->optionProgressBar() ? "On" : "Off") << '\n'
2113                << "MinefieldGenerationAnimation" << ' ' << preferences->optionMinefieldGenerationAnimationString() << '\n'
2114                << "AskToSaveMatchScoreBehaviour" << ' ' << (uchar)preferences->optionAskToSaveMatchScoreBehaviour() << '\n';
2115     }
2116 
2117     {
2118         QScopedPointer<QFile> fileLanguage( new QFile(dirAppData.absoluteFilePath("libreminesDefaultLanguage.txt")) );
2119         fileLanguage->open(QIODevice::WriteOnly);
2120 
2121         QTextStream stream(fileLanguage.get());
2122         stream << preferences-> optionsLanguage();
2123     }
2124 }
2125 
vUpdatePreferences()2126 void LibreMinesGui::vUpdatePreferences()
2127 {
2128     const QList<int> keys = preferences->optionKeyboardControllerKeys();
2129 
2130     controller.keyLeft = keys.at(0);
2131     controller.keyUp = keys.at(1);
2132     controller.keyRight = keys.at(2);
2133     controller.keyDown = keys.at(3);
2134     controller.keyReleaseCell= keys.at(4);
2135     controller.keyFlagCell = keys.at(5);
2136     controller.keyCenterCell = keys.at(6);
2137 
2138     controller.valid = true;
2139     for (int i=0; i<keys.size()-1; ++i)
2140     {
2141         for(int j=i+1; j<keys.size(); ++j)
2142         {
2143             controller.valid &= keys[i] != keys[j];
2144         }
2145     }
2146     controller.valid &= !keys.contains(-1);
2147 
2148     if(preferences->optionUsername().isEmpty())
2149 #ifdef Q_OS_WINDOWS
2150     {
2151         preferences->setOptionUsername(qgetenv("USERNAME"));
2152     }
2153 #else
2154     {
2155         preferences->setOptionUsername(qgetenv("USER"));
2156     }
2157 #endif
2158 }
2159