qt/MainForm.cpp: Unneeded variable removed (take it easy ;-) - furthermore it made...
[mines3d.git] / ui / qt / MainForm.cpp
blob4a60464a939b2579ebe847ef57b8f4cec252ed2e
1 /*
2 * File: MainForm.cpp
3 * Created: 7.1.2010
4 * Author: Petr Kubizňák
5 * Purpose:
6 */
8 /* -------------------------------------------------------------------------- */
10 #include "MainForm.h"
11 #include "../../asserts.h"
12 #include "../../exceptions/GeneralException.h"
13 #include <QtGui/QPushButton>
14 #include <QtGui/QLabel>
15 #include <QtGui/QMouseEvent>
16 #include <QtGui/QInputDialog>
17 #include <QtGui/QMessageBox>
18 #include <cstdio>
19 #include <iostream>
20 #include <fstream>
21 using namespace std;
23 /***************************** CLASS FieldButton ******************************/
25 FieldButton::FieldButton(MainForm *mainForm, int posX, int posY, int posZ) : QPushButton(mainForm) {
26 this->mainForm = mainForm;
28 //obecne nastaveni
29 this->posX = posX;
30 this->posY = posY;
31 this->posZ = posZ;
32 this->setText(" ");
33 this->setCheckable(true);
34 this->setFocusPolicy(Qt::NoFocus);
35 this->setMinimumWidth(23);
36 this->setMinimumHeight(23);
38 //rozmery komponent
39 QSizePolicy sizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
40 sizePolicy.setHorizontalStretch(0);
41 sizePolicy.setVerticalStretch(0);
42 sizePolicy.setHeightForWidth(true);
43 this->setSizePolicy(sizePolicy);
45 //fonty: normalni a zvyrazneny (pro oznacena tlacitka)
46 fontNormal = new QFont(this->font());
47 fontHighlight = new QFont(this->font());
48 fontHighlight->setUnderline(true);
49 fontHighlight->setBold(true);
52 /* -------------------------------------------------------------------------- */
54 FieldButton::~FieldButton(void) {
55 delete fontNormal;
56 delete fontHighlight;
59 /* -------------------------------------------------------------------------- */
61 /* stisk tlacitka */
62 void FieldButton::onClick(bool b) {
63 this->setChecked(true);
64 if(b) mainForm->uncover(posZ, posY, posX);
67 /* -------------------------------------------------------------------------- */
69 /* udalost najeti mysi nad tlacitko */
70 void FieldButton::enterEvent(QEvent *e) {
71 assert(mainForm);
72 mainForm->buttonHovered(posZ, posY, posX);
74 QPushButton::enterEvent(e);
77 /* -------------------------------------------------------------------------- */
79 /* udalost odjeti mysi z tlacitka */
80 void FieldButton::leaveEvent(QEvent *e) {
81 assert(mainForm);
82 mainForm->buttonLeft(posZ, posY, posX);
84 QPushButton::leaveEvent(e);
87 /* -------------------------------------------------------------------------- */
89 /* udalost stisku tlacitka */
90 void FieldButton::mousePressEvent(QMouseEvent *e) {
91 if(e->button() == Qt::RightButton) {
92 if(!this->isChecked()) mainForm->mark(posZ, posY, posX);
95 if(e->button() == Qt::MiddleButton )
97 mainForm->locked = !mainForm->locked;
100 QPushButton::mousePressEvent(e);
103 /* -------------------------------------------------------------------------- */
105 /* zvyrazni tlacitko zmenou fontu */
106 void FieldButton::highlight(void) {
107 this->setFont(*fontHighlight);
110 /* -------------------------------------------------------------------------- */
112 /* "odvyrazni" tlacitko zpet */
113 void FieldButton::unhighlight(void) {
114 this->setFont(*fontNormal);
118 /*********************************/// CLASS ///********************************/
120 /**************************** Class MarksIndicator ****************************/
122 /* -------------------------------------------------------------------------- */
124 MarksIndicator::MarksIndicator(QWidget *parent, int total) : QLabel(parent) {
125 this->total = total;
126 this->marked = 0;
127 fontNormal = new QFont(this->font());
128 fontBold = new QFont(this->font());
129 fontBold->setBold(true);
132 /* -------------------------------------------------------------------------- */
134 MarksIndicator::~MarksIndicator(void) {
135 delete fontNormal;
136 delete fontBold;
139 /* -------------------------------------------------------------------------- */
141 /* prekresli komponentu */
142 void MarksIndicator::repaint(void) {
143 char str[20];
144 sprintf(str, "Marks: %d / %d ", marked, total);
145 this->setText(str);
146 this->setFont(marked <= total ? *fontNormal : *fontBold);
149 /* -------------------------------------------------------------------------- */
151 /* nastavi celkovy pocet vlajecek, totez vraci */
152 int MarksIndicator::setTotal(int value) {
153 total = value;
154 repaint();
155 return value;
158 /* -------------------------------------------------------------------------- */
160 /* nastavi oznaceny pocet vlajecek, totez vraci */
161 int MarksIndicator::setMarked(int value) {
162 marked = value;
163 repaint();
164 return value;
167 /*********************************/// CLASS ///********************************/
169 /******************************* Class MainForm *******************************/
171 //makro pro otestovani existence tlacitka se zadanym umistenim
172 #define TEST_COORDS(l,r,c) { assert(l>=0 && l<this->layers); \
173 assert(r>=0 && r<this->rows); assert(c>=0 && c<this->cols); }
175 //nazev souboru s nastavenim
176 #define FILE_SETTINGS ".mines3d_settings"
177 #define FILE_SCORES_ROOKIE ".mines3d_scores_rookie"
178 #define FILE_SCORES_ADVANCED ".mines3d_scores_advanced"
179 #define FILE_SCORES_SUICIDE ".mines3d_scores_suicide"
180 #define FILE_SCORES_CUSTOM ".mines3d_scores_custom"
182 /* -------------------------------------------------------------------------- */
184 MainForm::MainForm(QWidget *parent) : QMainWindow(parent) {
185 ui.setupUi(this);
187 //zrusime zbytecne okraje
188 ui.scrollArea->setContentsMargins(0,0,0,0);
189 ui.scrollAreaWidgetContents->setContentsMargins(0,0,0,0);
190 ui.horizontalLayout->setContentsMargins(0,0,0,0);
191 ui.boardWidget->setContentsMargins(0,0,0,0);
192 ui.verticalLayout_3->setContentsMargins(0,0,0,0);
194 //status bar
195 marksIndicator = new MarksIndicator(this);
196 statusBar()->addPermanentWidget(marksIndicator);
198 //napojeni signalu na sloty
199 QObject::connect(ui.actionNew, SIGNAL(triggered()), this, SLOT(actionNew_Trigger()));
200 QObject::connect(ui.actionSettings, SIGNAL(triggered()), this, SLOT(actionSettings_Trigger()));
201 QObject::connect(ui.actionScores, SIGNAL(triggered()), this, SLOT(actionScores_Trigger()));
202 QObject::connect(ui.actionAbout, SIGNAL(triggered()), this, SLOT(actionAbout_Trigger()));
204 board = NULL;
205 settingsFrm = NULL;
206 scoresFrm = NULL;
207 aboutFrm = NULL;
209 tc = new Thread_controller();
210 sql_c = new Sql_connector();
211 locked = false;
213 //nacteme nastaveni ze souboru, pri neuspechu pouzijeme vychozi hodnoty
214 if(!loadSettings()) {
215 layers = PRESETS[0][0];
216 rows = PRESETS[0][1];
217 cols = PRESETS[0][2];
218 mines = PRESETS[0][3];
220 //nacteme vysledky predchozich her
221 loadScores();
222 name = "<unknown>"; //vychozi jmeno uzivatele (pri zapisu do vysledku)
224 //vyrobime novou hru
225 newGame();
228 /* -------------------------------------------------------------------------- */
230 MainForm::~MainForm() {
231 freeGui();
232 saveSettings();
233 saveScores();
234 delete settingsFrm;
235 delete scoresFrm;
236 delete aboutFrm;
237 delete marksIndicator;
240 /* -------------------------------------------------------------------------- */
242 /* nacte nastaveni ze standardniho souboru, vraci uspech operace */
243 bool MainForm::loadSettings(void) {
244 ifstream ifs(FILE_SETTINGS, ifstream::in);
245 if(!ifs.good()) return false;
246 ifs >> layers >> rows >> cols >> mines;
247 if(!ifs.good() || layers<=0 || rows<=0 || cols<=0 || mines<=0) return false;
248 ifs.close();
249 return true;
252 /* -------------------------------------------------------------------------- */
254 /* ulozi nastaveni do standardniho souboru, vraci uspech operace */
255 bool MainForm::saveSettings(void) {
256 ofstream ofs(FILE_SETTINGS, ofstream::out);
257 if(!ofs.good()) return false;
258 ofs << layers << " " << rows << " " << cols << " " << mines << endl << endl;
259 if(!ofs.good()) return false;
260 ofs.close();
261 return true;
264 /* -------------------------------------------------------------------------- */
266 /* nacte vsechna skore, vraci uspech operace */
267 bool MainForm::loadScores(void) {
268 return loadScoresFile(scoreRookie, FILE_SCORES_ROOKIE) &
269 loadScoresFile(scoreAdvanced, FILE_SCORES_ADVANCED) &
270 loadScoresFile(scoreSuicide, FILE_SCORES_SUICIDE) &
271 loadScoresFile(scoreCustom, FILE_SCORES_CUSTOM);
274 /* -------------------------------------------------------------------------- */
276 /* ulozi vsechna skore, vraci uspech operace */
277 bool MainForm::saveScores(void) {
278 return saveScoresFile(scoreRookie, FILE_SCORES_ROOKIE) &
279 saveScoresFile(scoreAdvanced, FILE_SCORES_ADVANCED) &
280 saveScoresFile(scoreSuicide, FILE_SCORES_SUICIDE) &
281 saveScoresFile(scoreCustom, FILE_SCORES_CUSTOM);
284 /* -------------------------------------------------------------------------- */
286 /* nacte skore ze zadaneho souboru do daneho listu, vraci uspech operace */
287 bool MainForm::loadScoresFile(list<ScoreRecord> &scoresList, const char *filename) {
288 ifstream ifs(filename, ifstream::in);
289 if(!ifs.good()) return false;
290 //dokud je co cist, cteme soubor do struktury
291 while(true) {
292 ScoreRecord scoreItem;
293 ifs >> scoreItem.time;
294 ifs.ignore(1); //ignorujeme mezeru
295 ifs.getline(scoreItem.name, sizeof(scoreItem.name));
296 if(!ifs.good()) {
297 ifs.close();
298 return true;
300 scoresList.push_back(scoreItem);
304 /* -------------------------------------------------------------------------- */
306 /* ulozi skore do zadaneho souboru z daneho listu, vraci uspech operace */
307 bool MainForm::saveScoresFile(list<ScoreRecord> &scoresList, const char *filename) {
308 ofstream ofs(filename, ofstream::out);
309 if(!ofs.good()) return false;
310 //zapiseme strukturu do souboru
311 for(list<ScoreRecord>::iterator it = scoresList.begin(); it != scoresList.end(); it++)
312 ofs << (*it).time << " " << (*it).name << endl;
313 ofs << endl;
314 if(!ofs.good()) return false;
315 ofs.close();
316 return true;
319 /* -------------------------------------------------------------------------- */
321 /* vraci true, pokud "a" predchazi pred "b" (podle casu), jinak false */
322 bool MainForm::cmpScoreByTime(const ScoreRecord &a, const ScoreRecord &b) {
323 return a.time < b.time;
326 /* -------------------------------------------------------------------------- */
328 /* vyrobi tlacitka a dalsi komponenty potrebne pro hru
329 * jako meze cyklu pouziva hodnoty layers, rows, cols a mines! */
330 void MainForm::buildGui(void) {
331 layerWidget = new QWidget * [layers];
332 layerGrid = new QGridLayout * [layers];
333 fieldBtn = new FieldButton *** [layers];
334 for(int i=0; i<layers; i++) {
335 layerWidget[i] = new QWidget(ui.scrollAreaWidgetContents);
336 ui.verticalLayout_3->addWidget(layerWidget[i]);
337 layerGrid[i] = new QGridLayout(layerWidget[i]);
338 layerGrid[i]->setHorizontalSpacing(0);
339 layerGrid[i]->setVerticalSpacing(0);
341 fieldBtn[i] = new FieldButton ** [rows];
342 for(int j=0; j<rows; j++) {
343 fieldBtn[i][j] = new FieldButton * [cols];
344 for(int k=0; k<cols; k++) {
345 fieldBtn[i][j][k] = new FieldButton(this, k, j, i);
346 layerGrid[i]->addWidget(fieldBtn[i][j][k], j, k);
347 QObject::connect(fieldBtn[i][j][k], SIGNAL(clicked(bool)), fieldBtn[i][j][k], SLOT(onClick(bool)));
352 tc->update_data(rows,cols,layers,board);
353 tc->run_thread();
356 /* -------------------------------------------------------------------------- */
358 /* uvolni prostredky alokovane metodou buildGui()
359 * jako meze cyklu pouziva rozmery v board! */
360 void MainForm::freeGui(void) {
361 for(int i=0; i<board->getLayersCount(); i++) {
362 for(int j=0; j<board->getRowsCount(); j++) {
363 for(int k=0; k<board->getColsCount(); k++) delete fieldBtn[i][j][k];
364 delete [] fieldBtn[i][j];
366 delete [] fieldBtn[i];
368 delete [] fieldBtn;
370 for(int i=0; i<board->getLayersCount(); i++) {
371 delete layerGrid[i];
372 delete layerWidget[i];
374 delete [] layerGrid;
375 delete [] layerWidget;
378 /* -------------------------------------------------------------------------- */
380 /* volano tridou FieldButton pri udalosti enterEvent, zvyrazni souvisejici tlacitka */
381 void MainForm::buttonHovered(int l, int r, int c) {
382 TEST_COORDS(l,r,c)
384 if(gameOver) return;
386 tc->mutex_lock();
387 if (!locked)
389 tc->selected[0] = l;
390 tc->selected[1] = r;
391 tc->selected[2] = c;
392 if ( tc->ptf != 0 )tc->ptf();
394 tc->selected[3] = l;
395 tc->selected[4] = r;
396 tc->selected[5] = c;
397 tc->mutex_unlock();
399 for(int nl=l-1; nl<=l+1; nl++) {
400 if(nl<0 || nl>=this->layers) continue;
401 for(int nr=r-1; nr<=r+1; nr++) {
402 if(nr<0 || nr>=this->rows) continue;
403 for(int nc=c-1; nc<=c+1; nc++) {
404 if(nc<0 || nc>=this->cols) continue;
405 fieldBtn[nl][nr][nc]->highlight();
411 /* -------------------------------------------------------------------------- */
413 /* volano tridou FieldButton pri udalosti leaveEvent, odzvyrazni souvisejici tlacitka */
414 void MainForm::buttonLeft(int l, int r, int c) {
415 TEST_COORDS(l,r,c)
417 if(gameOver) return;
419 for(int nl=l-1; nl<=l+1; nl++) {
420 if(nl<0 || nl>=this->layers) continue;
421 for(int nr=r-1; nr<=r+1; nr++) {
422 if(nr<0 || nr>=this->rows) continue;
423 for(int nc=c-1; nc<=c+1; nc++) {
424 if(nc<0 || nc>=this->cols) continue;
425 fieldBtn[nl][nr][nc]->unhighlight();
431 /* -------------------------------------------------------------------------- */
433 /* volano tridou FieldButton pri stisku tlacitka, odkryje souvisejici tlacitka */
434 void MainForm::uncover(int l, int r, int c) {
435 TEST_COORDS(l,r,c)
436 char numStr[10];
438 startTime(); //pokud jeste nemerime cas, zacneme nyni
440 //pole je oznacene jako zaminovane, nejde odkryt
441 if(board->getField(l,r,c).hasMark()) {
442 fieldBtn[l][r][c]->setChecked(false);
443 return;
446 //odkryjeme pole
447 if(board->uncover(l, r, c)) { //VYBUCH!
448 fieldBtn[l][r][c]->setText("!!");
449 setGameOver(false);
450 } else {
451 int num = board->getField(l,r,c).getNeighboursCnt();
452 if(num) {
453 sprintf(numStr, "%d", num);
454 fieldBtn[l][r][c]->setText(numStr); //vypiseme pocet min v okoli
455 } else { //0 min -> musime zaktualizovat celou desku
456 for(int i=0; i<this->layers; i++)
457 for(int j=0; j<this->rows; j++)
458 for(int k=0; k<this->cols; k++) {
459 fieldBtn[i][j][k]->setChecked(!board->getField(i,j,k).isCovered());
460 if(fieldBtn[i][j][k]->isChecked()) {
461 num = board->getField(i,j,k).getNeighboursCnt();
462 sprintf(numStr, "%d", num);
463 if(num) fieldBtn[i][j][k]->setText(numStr);
467 if(board->isCleared()) setGameOver(true);
471 /* -------------------------------------------------------------------------- */
473 /* volano tridou FieldButton pri stisku praveho tlacitka, (od)znaci dane policko */
474 void MainForm::mark(int l, int r, int c) {
475 TEST_COORDS(l,r,c)
477 startTime(); //pokud jeste nemerime cas, zacneme nyni
479 Field &field = board->getField(l, r, c);
480 if(!field.isCovered()) return;
481 field.setMark(!field.hasMark());
482 fieldBtn[l][r][c]->setText(field.hasMark() ? "M" : " "); //znacka
484 marksIndicator->setMarked(board->getMarkedCount());
487 /* -------------------------------------------------------------------------- */
489 /* nastavi pocatecni cas hry */
490 void MainForm::startTime(void) {
491 if(tStart==-1) tStart = time(NULL); //pocatek mereni casu
494 /* -------------------------------------------------------------------------- */
496 /* vytvori novou hru s aktualnim nastavenim */
497 void MainForm::newGame(void) {
498 if(board)
500 tc->mutex_lock();
501 freeGui(); //uvolni stare gui (jeste podle stareho nastaveni)
502 delete board; board = NULL;
503 tc->b = 0;
504 locked = false;
505 tc->mutex_unlock();
507 try {
508 board = new Board(rows, cols, layers, mines);
509 } catch(GeneralException e) {
510 char text[200];
511 snprintf(text, 199, "An error occured:\n%s\nUnable to start game.", e.errText.data());
512 QMessageBox::warning(this, "Mines3D", text, QMessageBox::Ok);
513 return;
515 ui.scrollAreaWidgetContents->setEnabled(true);
516 gameOver = false;
517 marksIndicator->setTotal(mines);
518 marksIndicator->setMarked(0);
519 buildGui();
520 tStart = -1; //indikuje, ze jeste nezacalo mereni (zacne prvnim kliknutim)
523 /* -------------------------------------------------------------------------- */
525 /* ukonci hru -> znemozni stisk tlacitek atp.;
526 * atr success je true, pokud uzivatel vyhral, jinak false */
527 void MainForm::setGameOver(bool success) {
528 gameOver = true;
529 tStop = time(NULL);
530 ui.scrollAreaWidgetContents->setEnabled(false);
532 //odvyraznime vsechna tlacitka a vykreslime polohy min
533 for(int i=0; i<this->layers; i++) {
534 for(int j=0; j<this->rows; j++)
535 for(int k=0; k<this->cols; k++) {
536 fieldBtn[i][j][k]->unhighlight();
537 if(board->getField(i,j,k).hasMine())
538 fieldBtn[i][j][k]->setText(success ? "M" : "!!");
541 //vyhodnoceni vysledku
542 if(!success) {
543 statusBar()->showMessage("Oooops, you stepped on a mine which exploxed!", 10000);
544 } else {
545 char str[100];
546 const int tTotal = tStop-tStart;
547 sprintf(str, "Very well, mister! You cleared the board in %d:%02d.", tTotal/60, tTotal%60);
548 statusBar()->showMessage(str, 10000);
549 processTopTen(tTotal); //zapis do tabulky (pokud je cas v top10)
553 /* -------------------------------------------------------------------------- */
555 /* pokud je zadany cas v top10, umozni zapis do tabulky a vraci true, jinak false */
556 bool MainForm::processTopTen(int time) {
557 bool ok;
559 list<ScoreRecord> *l;
560 switch(board->getMode()) {
561 case Board::ModeRookie: l=&scoreRookie; break;
562 case Board::ModeAdvanced: l=&scoreAdvanced; break;
563 case Board::ModeSuicide: l=&scoreSuicide; break;
564 case Board::ModeCustom: l=&scoreCustom; break;
565 default: return false;
568 l->sort(cmpScoreByTime); //seradime vzestupne podle casu
569 //je-li v seznamu jeste misto nebo nejhorsi cas je horsi, nez soucasny, jedna se o top10 -> pridame zaznam
570 if(l->size() < 10 || l->back().time > time) {
572 //vyzveme uzivatele k zadani jmena
573 QString text = QInputDialog::getText(this, "Top 10!", "You have made the top 10!\nPlease type in your name:",
574 QLineEdit::Normal, name, &ok);
575 //pokud bylo stisknuto OK
576 if(ok) name = text; //zapamatujem si vlozene jmeno
577 else return false; //uzivatel si nepreje vlozit zaznam
579 sql_c->connect(); // nahrani dat do databaze
581 sql_c->insertScore(name.toUtf8().data(), time, board->getMode());
582 sql_c->disconnect();
584 ScoreRecord scoreItem;
585 snprintf(scoreItem.name, 40, "%s", name.toUtf8().data());
586 scoreItem.time = time;
587 if(l->size() >= 10) l->pop_back(); //neni jiz misto -> vymazeme nejhorsi cas
588 l->push_back(scoreItem); //vlozime zaznam
589 if(scoresFrm) scoresFrm->replaceModel(board->getMode(), l); //zaktualizujeme model dat ve formulari vysledku
590 actionScores_Trigger(); //zobrazime vysledky
591 return true;
593 return false; //v seznamu neni misto (cas nebyl dostatecne rychly)
596 /* -------------------------------------------------------------------------- */
598 /* stisk tlacitka "New" v menu */
599 void MainForm::actionNew_Trigger(void) {
600 newGame();
603 /* -------------------------------------------------------------------------- */
605 /* stisk tlacitka "Settings..." v menu */
606 void MainForm::actionSettings_Trigger(void) {
607 if(!settingsFrm) settingsFrm = new SettingsForm();
609 //zobrazime aktualni nastaveni
610 int mode = board ? board->getMode() : Board::ModeRookie;
611 switch(mode) {
612 case Board::ModeRookie:
613 settingsFrm->ui.rookieRadio->setChecked(true);
614 settingsFrm->rookieRadio_Clicked();
615 break;
616 case Board::ModeAdvanced:
617 settingsFrm->ui.advancedRadio->setChecked(true);
618 settingsFrm->advancedRadio_Clicked();
619 break;
620 case Board::ModeSuicide:
621 settingsFrm->ui.suicideRadio->setChecked(true);
622 settingsFrm->suicideRadio_Clicked();
623 break;
624 case Board::ModeCustom:
625 settingsFrm->ui.customRadio->setChecked(true);
626 settingsFrm->ui.layersSpinBox->setValue(this->layers);
627 settingsFrm->ui.rowsSpinBox->setValue(this->rows);
628 settingsFrm->ui.columnsSpinBox->setValue(this->cols);
629 settingsFrm->ui.minesSpinBox->setValue(this->mines);
630 settingsFrm->customRadio_Clicked();
631 break;
633 //zobrazime dialog a vyhodnotime odpoved
634 if(settingsFrm->exec() == QDialog::Accepted) {
635 layers = settingsFrm->ui.layersSpinBox->value();
636 rows = settingsFrm->ui.rowsSpinBox->value();
637 cols = settingsFrm->ui.columnsSpinBox->value();
638 mines = settingsFrm->ui.minesSpinBox->value();
639 newGame();
643 /* -------------------------------------------------------------------------- */
645 /* stisk tlacitka "Hall of Fame" v menu */
646 void MainForm::actionScores_Trigger(void) {
647 if(!scoresFrm) scoresFrm = new ScoresForm(&scoreRookie, &scoreAdvanced, &scoreSuicide, &scoreCustom);
648 scoresFrm->exec(board->getMode());
651 /* -------------------------------------------------------------------------- */
653 void MainForm::actionAbout_Trigger(void) {
654 if(!aboutFrm) aboutFrm = new AboutForm();
655 aboutFrm->exec();
658 /* -------------------------------------------------------------------------- */