#include "window.h" myWindow::myWindow(QWidget *parent) : QMainWindow(parent){ createActions(); createMenus(); createToolbarAndBoard(); createStatusBar(); newGame(0); /* * centralWidget is a widget of type QWidget which will be the centralWidget of myWindow class. */ centralWidget = new QWidget(); //Allocate memory for the QWidget object centralWidget setCentralWidget(centralWidget); //Set centralWidget to be the central widget of the class (myWindow) centralWidget->setLayout(layout); //Set centralWidget's layout to layout setWindowTitle("My Cool Minesweeper Game!![*]"); //Set window's title } /* * createPushButtonGrid takes two integer arguments and returns a QWidget * createPushButtonGrid creates a vertical box that contains f_rows horizontal boxes * Each horizontal box holds f_cols buttons. Then the vertical box is added to a widget * and the widget is returned. */ QWidget* myWindow::createPushButtonGrid(int pLevel, int f_cols, int f_rows){ buttons.push_back(QVector >(f_rows)); QWidget* toBeReturned = new QWidget(); //Allocate memory for the QWidget object toBeReturned QVBoxLayout* buttonGrid = new QVBoxLayout(); //Allocate memory for the QVBoxLayout object buttonGrid for(int i=0; i()); QHBoxLayout* hbox = new QHBoxLayout(); //Allocate memory for a QHBoxLayout object hbox for(int j=0; j setFixedSize(SIZE_OF_BUTTON,SIZE_OF_BUTTON); //Set fixed (can't be changed later) size to SIZE_OF_BUTTON connect(buttons[pLevel][i][j], SIGNAL(released()), this, SLOT(handleButtonReleased())); connect(buttons[pLevel][i][j], SIGNAL(rightButtonClicked()), this, SLOT(handleRightButton())); hbox -> addWidget(buttons[pLevel][i][j]); //Add button widget to hbox } hbox -> setSpacing(0); //Set spacing of hbox to 0 so buttons will be close to one another buttonGrid->addLayout(hbox); //Add hbox layout to buttonGrid } buttonGrid -> setSpacing(0); //Set buttonGrid's spacing to 0 toBeReturned->setLayout(buttonGrid); //Set buttonGrid to be the layout of toBeReturned widget toBeReturned->setFixedSize(SIZE_OF_BUTTON*f_cols,SIZE_OF_BUTTON*f_rows); //Set fixed size of toBeReturned return toBeReturned; } void myWindow::createToolbarAndBoard(){ /* * levelSelect is a layout of QHBoxLayout type, meaning it is a horizontal box that holds * different widgets. levelSelect is used to hold the buttons for level selection. */ levelSelect = new QHBoxLayout(); //Allocate memory for the QHBoxLayout object levelSelect QPushButton *easy = new QPushButton(tr("&Easy")); //Create button easy connect(easy, SIGNAL(clicked()), this, SLOT(handleEasy())); //Conect button with appropriate slot easy->setStatusTip(tr("Start new game at easy level")); //Set status bar tip QPushButton *moderate = new QPushButton(tr("&Moderate")); //Create button moderate connect(moderate, SIGNAL(clicked()), this, SLOT(handleModerate())); //Conect button with appropriate slot moderate->setStatusTip(tr("Start new game at moderate level")); //Set status bar tip QPushButton *hard = new QPushButton(tr("&Hard")); //Create button hard connect(hard, SIGNAL(clicked()), this, SLOT(handleHard())); //Conect button with appropriate slot hard->setStatusTip(tr("Start new game at hard level")); //Set status bar tip levelSelect->addWidget(easy); //Add button easy to levelSelect Layout levelSelect->addWidget(moderate); //Add button moderate to levelSelect Layout levelSelect->addWidget(hard); //Add button hard to levelSelect Layout /* * boardSelect is a widget of QStackedWidget type. It has 3 layers of widgets. At any time only one is vissible. * Each widget in every level contains a button grid with the appropriate amount of buttons for each level. */ boardSelect = new QStackedWidget(); //Allocate memory for the QStackedWidget object boardSelect boardSelect->addWidget(createPushButtonGrid(0,EASY_COLUMNS,EASY_ROWS)); //Add a widget with button grid for easy level boardSelect->addWidget(createPushButtonGrid(1,MODERATE_COLUMNS,MODERATE_ROWS)); //Add a widget with button grid for moderate level boardSelect->addWidget(createPushButtonGrid(2,HARD_COLUMNS,HARD_ROWS)); //Add a widget with button grid for hard level boardSelect->setCurrentIndex(0); //Set stack index to 0, board for easy level will be shown /* * Sxolia edw! * */ timer= new QTimer(); lcdNumber1 = new QLCDNumber; //show time lcdNumber1->setDigitCount(4); currentTime=0; connect(timer, SIGNAL(timeout()), this, SLOT(updateTimer())); /* * layout is a layout of type QVBoxLayout,meaning it is a vertical box that holds * different widgets and child layouts. layout should contain every widget and layout * the basic window has. */ layout = new QVBoxLayout; //Allocate memory for the QVBoxLayout object layout layout->addLayout(levelSelect); //Add levelSelect as a child layout to layout layout->addWidget(lcdNumber1); //Add clock widget to layout layout->addWidget(boardSelect); //Add boardSelect widget to layout layout->setSpacing(0); //Set layout's spacing to 0 } /* * createActions allocates memory for the actions of save,save as, open, exit, * about and about qt. In this funtion these actions are created, given their * properties and connected to their different signals */ void myWindow::createActions(){ openAct = new QAction("&Open",this); openAct->setShortcuts(QKeySequence::Open); openAct->setStatusTip(tr("Open a saved game")); connect(openAct, SIGNAL(triggered()), this, SLOT(open())); saveAct = new QAction("&Save",this); saveAct->setShortcuts(QKeySequence::Save); saveAct->setStatusTip(tr("Save progress")); connect(saveAct, SIGNAL(triggered()), this, SLOT(save())); saveAsAct = new QAction(tr("Save &As..."), this); saveAsAct->setShortcuts(QKeySequence::SaveAs); saveAsAct->setStatusTip(tr("Save progress under a new name")); connect(saveAsAct, SIGNAL(triggered()), this, SLOT(saveAs())); exitAct = new QAction(tr("E&xit"), this); exitAct->setShortcuts(QKeySequence::Quit); exitAct->setStatusTip(tr("Exit the application")); connect(exitAct, SIGNAL(triggered()), qApp, SLOT(closeAllWindows())); aboutAct = new QAction(tr("&About"),this); aboutAct->setStatusTip(tr("Show the application's About box")); connect(aboutAct, SIGNAL(triggered()), this, SLOT(about())); aboutQtAct = new QAction(tr("About &Qt"),this); aboutQtAct->setStatusTip(tr("Show the Qt library's About box")); connect(aboutQtAct, SIGNAL(triggered()), qApp, SLOT(aboutQt())); } /* * createMenus allocates memory for the menus file,help and the submenu new * In this funtion these menus are created, given their properties and * defferent actions are added to them. Also the actions added to the new * submenu are created here. */ void myWindow::createMenus() { fileMenu = menuBar()->addMenu(tr("&File")); newMenu = fileMenu->addMenu(tr("&New")); newEasy = new QAction("New &Easy Game (10X10)",newMenu); newEasy->setStatusTip(tr("New easy game 10 bmbs")); connect(newEasy, SIGNAL(triggered()), this, SLOT(handleEasy())); newModerate = new QAction("New &Moderate Game (12X15)",newMenu); newModerate->setStatusTip(tr("New moderate game 40 bmbs")); connect(newModerate, SIGNAL(triggered()), this, SLOT(handleModerate())); newHard = new QAction("New &Hard Game (15X20)",newMenu); newHard->setStatusTip(tr("New hard game 70 bmbs")); connect(newHard, SIGNAL(triggered()), this, SLOT(handleHard())); newMenu->addAction(newEasy); newMenu->addAction(newModerate); newMenu->addAction(newHard); fileMenu->addMenu(newMenu); fileMenu->addAction(openAct); fileMenu->addAction(saveAct); fileMenu->addAction(saveAsAct); fileMenu->addSeparator(); fileMenu->addAction(exitAct); menuBar()->addSeparator(); helpMenu = menuBar()->addMenu(tr("&Help")); helpMenu->addAction(aboutAct); helpMenu->addAction(aboutQtAct); } void myWindow::handleEasy(){ //Button easy was pressed if(maybeSave()){ setWindowModified(false); newGame(0); } } void myWindow::handleModerate(){ //Button moderate was pressed if(maybeSave()){ setWindowModified(false); newGame(1); } } void myWindow::handleHard(){ //Button moderate was pressed if(maybeSave()){ setWindowModified(false); newGame(2); } } void myWindow::newGame(int pLevel){ level = pLevel; //Set level switch(level){ case 0: //Set all buttons to initial state for(int i=0; isetFlat(false); buttons[level][i][j]->setFlaged(false); buttons[level][i][j]->setIcon(QIcon()); } } board = new Board(BOMBS_ON_EASY,EASY_ROWS,EASY_COLUMNS); //Create a new board boardSelect->setCurrentIndex(level); //Set boardSelect's index to level (0) to show the board for easy this->setFixedSize(250,320); //Set window's size break; case 1: //Set all buttons to initial state for(int i=0; isetFlat(false); buttons[level][i][j]->setFlaged(false); buttons[level][i][j]->setIcon(QIcon()); } } board = new Board(BOMBS_ON_MODERATE,MODERATE_ROWS,MODERATE_COLUMNS); //Create a new board boardSelect->setCurrentIndex(level); //Set boardSelect's index to level (1) to show the board for moderate this->setFixedSize(330,350); //Set window's size break; case 2: //Set all buttons to initial state for(int i=0; isetFlat(false); buttons[level][i][j]->setFlaged(false); buttons[level][i][j]->setIcon(QIcon()); } } board = new Board(BOMBS_ON_HARD,HARD_ROWS,HARD_COLUMNS); //Create a new board boardSelect->setCurrentIndex(level); //Set boardSelect's index to level (2) to show the board for hard this->setFixedSize(430,420); //Set window's size break; default: // should never be reached QMessageBox messageBox; messageBox.critical(0,"Error","An error has occured at newGame function!"); messageBox.setFixedSize(500,200); throw(EXCEPTION_EXECUTE_FAULT); } gameEnded = false; trueClickedCounter = 0; trueFlagCounter = 0; hasStarted=false; } void myWindow::open(){ //Action open was pressed QString fileName = QFileDialog::getOpenFileName(this); //Ask user to select a file if (!fileName.isEmpty()) { //User has selected a file if (isUntitled && !isWindowModified()){ //No progress was made in current game loadFile(fileName); //Load selected file } else { //There is unsaved progress in current game if(maybeSave()) //User wants to save? loadFile(fileName); //Load selected file } } } bool myWindow::save(){ //Action save was pressed if (isUntitled) { //There in no previously saved file of this game return saveAs(); //Create a new file and save there } else if(gameEnded){ //Don't save and ended game! QMessageBox messageBox; messageBox.critical(0,"Game ended!", "You can't save an ended game."); messageBox.setFixedSize(500,200); return true; } else { //This game has been saved before in a file return saveFile(curFile); //Replace old file with new } } bool myWindow::saveAs(){ //Action save as was pressed if(gameEnded){ //Don't save and ended game! QMessageBox messageBox; messageBox.critical(0,"Game ended!", "You can't save an ended game."); messageBox.setFixedSize(500,200); return true; } //Ask user for a name and directory of the save file QString fileName = QFileDialog::getSaveFileName(this, tr("Save As"), curFile); if (fileName.isEmpty()) //No name and directory were givven return false; return saveFile(fileName); //Save game } void myWindow::about(){ //Action about was pressed QMessageBox::about(this, tr("About Minesweeper"), tr("Write shit here!")); } void myWindow::wasModified(){ //Action about qt was pressed setWindowModified(true); } void myWindow::createStatusBar(){ //Initialize the status bar statusBar()->showMessage(tr("Good luck!")); } bool myWindow::maybeSave(){ if (isWindowModified() && (!gameEnded) ){ //There has been progress on this game QMessageBox::StandardButton ret; //Ask user wether to save or not. ret = QMessageBox::warning(this, tr("Minesweeper"), tr("The document has been modified.\n" "Do you want to save your changes?"), QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); if (ret == QMessageBox::Save) //User wants to save return save(); //Save else if (ret == QMessageBox::Cancel) //User wants to discard return false; } return true; } void myWindow::loadFile(const QString &fileName){ //Load a saved game for directory fileName QFile file(fileName); if (!file.open(QFile::ReadOnly | QFile::Text)) { //Try to open file //File could not be oppened QMessageBox::warning(this, tr("Minesweeper"), tr("Cannot read file %1:\n%2.") .arg(fileName) .arg(file.errorString())); return; } //File was successfully opened QTextStream in(&file); //Initialize a text stream (same as string stream) QString line = in.readLine(); //Read a line (first line) from the file if(line == "Minesweeper game"){ //If the line is correct this is indeed a Minesweeper game file int lLevel,lRows,lColumns; //Localy used variables lLevel = (in.readLine().toInt()); //Second line is the level switch (lLevel){ //Initialize rows and columns depending on the level case(0): lRows = EASY_ROWS; lColumns = EASY_COLUMNS; break; case(1): lRows = MODERATE_ROWS; lColumns = MODERATE_COLUMNS; break; case(2): lRows = HARD_ROWS; lColumns = HARD_COLUMNS; break; default: //Should never be reached QMessageBox messageBox; messageBox.critical(0,"Error","An error has occured while reading from the file!"); messageBox.setFixedSize(500,200); throw(EXCEPTION_READ_FAULT); } //Declare a vector, will hold the encrypted file data QVector< QVector > encrypted(lRows, QVector(lColumns)); int lRowCounter = 0; //Until all neccessery data were readen or reached the end of the file while( (lRowCounter < lRows) && (!in.atEnd()) ) { QString line = in.readLine(); //Read next line QStringList fields = line.split(","); //Split lines data in strings at every "," if(fields.size() < (lColumns-1)){ //Not enough data on this line //Should never be reached QMessageBox messageBox; messageBox.critical(0,"Error", "An error has occured while reading from the file!\n Either not enough data on the file or data of wrong format!"); messageBox.setFixedSize(500,200); throw(EXCEPTION_READ_FAULT); } for(int i=0; i > decrypted = board->getDecryption(encrypted); //Decrypt data newGame(lLevel); //Start a new game board = new Board(decrypted); //Create new board lRowCounter = 0; //Restart counter /*Read and recreate previously done progress */ //Until all neccessery data were readen or reached the end of the file while( (lRowCounter < lRows) && (!in.atEnd()) ) { QString line = in.readLine(); //Read next line QStringList fields = line.split(","); //Split lines data in strings at every "," if(fields.size() < (lColumns-1)){ //Not enough data on this line //Should never be reached QMessageBox messageBox; messageBox.critical(0,"Error", "An error has occured while reading from the file!\n Either not enough data on the file or data of wrong format!"); messageBox.setFixedSize(500,200); throw(EXCEPTION_READ_FAULT); } for(int i=0; iclick(); //Do it again break; case -1: //Means button at (lRowCounter,i) has been right-clicked before saving buttons[lLevel][lRowCounter][i]->rightButtonClicked(); //Do it again break; default: //Should never be reached QMessageBox messageBox; messageBox.critical(0,"Error", "An error has occured while reading from the file!\n Bad data!"); messageBox.setFixedSize(500,200); throw(EXCEPTION_READ_FAULT); } } ++lRowCounter; } /* Data reading is done, am I at the end of the file? */ if(!in.atEnd()){ //Should never be reached QMessageBox messageBox; messageBox.critical(0,"Warning", "There are more data at the end of the file!"); messageBox.setFixedSize(500,200); } file.close(); //Close file setCurrentFile(fileName); statusBar()->showMessage(tr("File loaded"), 2000); setWindowModified(false); //No changes done since loading the file return; } else{ //This is propably not a Minesweeper game file, first line was not Minesweeper game // should never be reached QMessageBox messageBox; messageBox.critical(0,"Error", "An error has occured while loading the file!\nThis is not a Minesweeper game file!"); messageBox.setFixedSize(500,200); throw(EXCEPTION_INVALID_HANDLE); } } bool myWindow::saveFile(const QString &fileName){ //Save game at fileName directory QFile file(fileName); if (!file.open(QFile::WriteOnly | QFile::Text)) { //Try to create an empty file //File could not be created QMessageBox::warning(this, tr("Minesweeper"), tr("Cannot write file %1:\n%2.") .arg(fileName) .arg(file.errorString())); return false; } //File was successfully created QTextStream out(&file); //Initialize a text stream (same as string stream) //Declare a vector and put the encrypted file data there QVector< QVector > encrypted = board->getEncryption(); out << "Minesweeper game" << endl; //First line is this to distinguish Minesweeper files out << level << endl; //Second line is the level /* * Next line are the encrypted data in this format: * b(0,0),b(1,0), ... ,b(columns-1,0) * b(1,1), ... ,b(columns-1,1) * . . * . . * . . * b(rows-1,0), ... ,b(rows-1,columns-1) */ for(int i=0; iisFlat()){ out << 1; } else if(buttons[level][i][j]->isFlaged()){ out << -1; } else out << 0; if(j != (encrypted.at(i).size()-1)) //Make sure we don't put semicolon at the end of the row out << "," ; } out << endl; } file.close(); //Close file setCurrentFile(fileName); statusBar()->showMessage(tr("File saved"), 2000); setWindowModified(false); return true; } void myWindow::setCurrentFile(const QString &fileName){ static int sequenceNumber = 1; isUntitled = fileName.isEmpty(); if (isUntitled) { curFile = tr("document%1.txt").arg(sequenceNumber++); } else { curFile = QFileInfo(fileName).canonicalFilePath(); } setWindowModified(false); setWindowFilePath(curFile); } void myWindow::handleButtonReleased(){ //A button has been left-clicked MineSweeperButton* sendedBy = (MineSweeperButton *) sender(); //Get the button if(!hasStarted){ timer->start(1000); hasStarted=true; } if(sendedBy->isFlaged() || sendedBy->isFlat()) //If the button is flaged do nothing return; setWindowModified(true); //Else there has been progress since last save switch(board->getNumberOfBombs(sendedBy->getRow(),sendedBy->getColumn())){ case 0: //If the button has no neighboring bombs { //Set the button's icon and size for 0 QPixmap pixmap(":/images/zero.png"); QIcon ButtonIcon(pixmap); sendedBy->setIcon(ButtonIcon); sendedBy->setIconSize(pixmap.rect().size()); ++trueClickedCounter; } break; case 1: //If the button has one neighboring bomb { //Set the button's icon and size for 1 QPixmap pixmap(":/images/one.png"); QIcon ButtonIcon(pixmap); sendedBy->setIcon(ButtonIcon); sendedBy->setIconSize(pixmap.rect().size()); ++trueClickedCounter; } break; case 2: //If the button has two neighboring bombs { //Set the button's icon and size for 2 QPixmap pixmap(":/images/two.png"); QIcon ButtonIcon(pixmap); sendedBy->setIcon(ButtonIcon); sendedBy->setIconSize(pixmap.rect().size()); ++trueClickedCounter; } break; case 3: //If the button has three neighboring bombs { //Set the button's icon and size for 3 QPixmap pixmap(":/images/three.png"); QIcon ButtonIcon(pixmap); sendedBy->setIcon(ButtonIcon); sendedBy->setIconSize(pixmap.rect().size()); ++trueClickedCounter; } break; case 4: //If the button has four neighboring bombs { //Set the button's icon and size for 4 QPixmap pixmap(":/images/four.png"); QIcon ButtonIcon(pixmap); sendedBy->setIcon(ButtonIcon); sendedBy->setIconSize(pixmap.rect().size()); ++trueClickedCounter; } break; case 5: //If the button has five neighboring bombs { //Set the button's icon and size for 5 QPixmap pixmap(":/images/five.png"); QIcon ButtonIcon(pixmap); sendedBy->setIcon(ButtonIcon); sendedBy->setIconSize(pixmap.rect().size()); ++trueClickedCounter; } break; case 6://If the button has six neighboring bombs { //Set the button's icon and size for 6 QPixmap pixmap(":/images/six.png"); QIcon ButtonIcon(pixmap); sendedBy->setIcon(ButtonIcon); sendedBy->setIconSize(pixmap.rect().size()); ++trueClickedCounter; } break; case 7: //If the button has seven neighboring bombs { //Set the button's icon and size for 7 QPixmap pixmap(":/images/seven.png"); QIcon ButtonIcon(pixmap); sendedBy->setIcon(ButtonIcon); sendedBy->setIconSize(pixmap.rect().size()); ++trueClickedCounter; } break; case 8: //If the button has eight neighboring bombs { //Set the button's icon and size for 8 QPixmap pixmap(":/images/eight.png"); QIcon ButtonIcon(pixmap); sendedBy->setIcon(ButtonIcon); sendedBy->setIconSize(pixmap.rect().size()); ++trueClickedCounter; } break; case -1: //If the button has a bomb { if(!gameEnded){ //Set the button's icon and size for activated bomb QPixmap pixmap(":/images/Activatedbomb.png"); QIcon ButtonIcon(pixmap); sendedBy->setIcon(ButtonIcon); sendedBy->setIconSize(pixmap.rect().size()); sendedBy->setFlat(true); //Player lost! lose(); } else{ //Set the button's icon and size for bomb QPixmap pixmap(":/images/bomb.png"); QIcon ButtonIcon(pixmap); sendedBy->setIcon(ButtonIcon); sendedBy->setIconSize(pixmap.rect().size()); } } break; default: // should never be reached QMessageBox messageBox; messageBox.critical(0,"Error", "Something went wrong in handelButtonReleased slot!"); messageBox.setFixedSize(500,200); throw(EXCEPTION_INVALID_HANDLE); } sendedBy->setFlat(true); //Set button to flat so we know it's been left-clicked if(trueClickedCounter == (buttons.at(level).size() * buttons.at(level).at(0).size() - board->getNbombs()) ){ if(!gameEnded) win(); } } void myWindow::handleRightButton(){ //A button has been right-clicked MineSweeperButton* sendedBy = (MineSweeperButton *) sender(); //Get the button if(sendedBy->isFlat()) //If the button is flat (has been left-clicked) do nothing return; setWindowModified(true); //Else there has been progress since last save if(!(sendedBy->isFlaged())){ //If the button is not already flagged //Set the button's icon and size to flag sendedBy->setFlaged(true); QPixmap pixmap(":/images/flag.png"); QIcon ButtonIcon(pixmap); sendedBy->setIcon(ButtonIcon); sendedBy->setIconSize(pixmap.rect().size()); if(board->getNumberOfBombs(sendedBy->getRow(),sendedBy->getColumn()) == -1){ ++trueFlagCounter; } else --trueFlagCounter; } else{ //The button is already flagged //Un-do the "flagging" sendedBy->setFlaged(false); sendedBy->setIcon(QIcon()); if(board->getNumberOfBombs(sendedBy->getRow(),sendedBy->getColumn()) == -1){ --trueFlagCounter; } else ++trueFlagCounter; } if(trueFlagCounter == board->getNbombs()){ if(!gameEnded) win(); } } void myWindow::lose(){ //Player lost gameEnded = true; revealAll(); QMessageBox messageBox; messageBox.critical(0,"You lost!", "Better luck next time... :)"); messageBox.setFixedSize(500,200); } void myWindow::win(){ //Player won gameEnded = true; revealAll(); QMessageBox messageBox; messageBox.critical(0,"You won!", "Great job! :)"); messageBox.setFixedSize(500,200); } void myWindow::updateTimer(){ currentTime++; lcdNumber1->display(currentTime); } void myWindow::revealAll() const{ for(int i=0; iisFlat()) ) buttons[level][i][j]->click(); } } }