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