* Fixed an issue with translations
[colorful-apps:puzzle-master.git] / mainwindow.cpp
1 #include "mainwindow.h"
2 #include "ui_mainwindow.h"
3 #include "jigsawpuzzleboard.h"
4 #include "jigsawpuzzleitem.h"
5 #include "util.h"
6 #include <QtGui>
7
8 #if defined(Q_WS_MAEMO_5)
9 #include <QtMaemo5>
10 #include <hildon-extras-1/hildon-extras/qt-he-wrapper.h>
11 #endif
12
13 #if defined(HAVE_OPENGL)
14 #include <QGLWidget>
15 #endif
16
17 #if defined(Q_OS_SYMBIAN)
18 #include <eikenv.h>
19 #include <eikappui.h>
20 #include <aknenv.h>
21 #include <aknappui.h>
22 #endif
23
24 MainWindow::MainWindow(QWidget *parent) :
25     QMainWindow(parent),
26     ui(new Ui::MainWindow),
27     timer(new QTimer(this)),
28     settings(new SettingsDialog(this)),
29     newgame(new NewGameDialog(this)),
30     highscores(new HighScoresDialog(this)),
31     board(new JigsawPuzzleBoard(this)),
32     _isPlaying(false),
33     _isPaused(false),
34     _wasPaused(false),
35     _canPause(false),
36     _currentScaleRatio(1)
37 {
38     ui->setupUi(this);
39
40     ui->btnPause->hide();
41     ui->actionPause->setVisible(false);
42     ui->btnSurrender->hide();
43     ui->actionSurrender->setVisible(false);
44     ui->btnHelp->hide();
45     ui->btnExit->hide();
46
47     timer->setInterval(1000);
48     connect(timer, SIGNAL(timeout()), this, SLOT(updateElapsedTimeLabel()));
49     ui->graphicsView->setResizeAnchor(QGraphicsView::AnchorViewCenter);
50     setFocus();
51
52     QColor bg = SettingsDialog::boardBackground();
53     board->setBackgroundBrush(QBrush(bg));
54     intro = new QGraphicsTextItem(tr("Please press the new game button!"));
55     intro->setDefaultTextColor(QColor(0xFFFFFF - bg.rgb()));
56     board->addItem(intro);
57
58     ui->graphicsView->setScene(board);
59 #if defined(HAVE_OPENGL)
60     ui->graphicsView->setViewport(new QGLWidget(QGLFormat(QGL::DoubleBuffer), this));
61 #endif
62
63 #if defined(MOBILE)
64     ui->graphicsView->setOptimizationFlag(QGraphicsView::DontAdjustForAntialiasing);
65     ui->graphicsView->setOptimizationFlag(QGraphicsView::DontSavePainterState);
66     ui->graphicsView->setViewportUpdateMode(QGraphicsView::SmartViewportUpdate);
67
68     ui->lblTime->hide();
69     ui->horizontalLayout->setContentsMargins(10, 0, 10, 0);
70 #endif
71
72 #if defined(Q_WS_MAEMO_5)
73     setFixedHeight(424);
74     setAttribute(Qt::WA_Maemo5AutoOrientation);
75     ui->btnFullscreen->setIcon(QIcon::fromTheme("general_fullsize"));
76 #endif
77
78 #if defined(Q_OS_SYMBIAN)
79     ui->btnFullscreen->hide();
80     ui->lblTime->show();
81     ui->btnExit->show();
82 #endif
83 }
84
85 MainWindow::~MainWindow()
86 {
87     delete ui;
88 }
89
90 void MainWindow::closeEvent(QCloseEvent *event)
91 {
92     if (QMessageBox::Yes == QMessageBox::question(this, tr("Are you sure?"), tr("Do you want to exit the game?"), QMessageBox::Yes, QMessageBox::No))
93     {
94         event->accept();
95         QApplication::instance()->quit();
96     }
97     event->ignore();
98 }
99
100 bool MainWindow::event(QEvent *event)
101 {
102     bool result = QMainWindow::event(event);
103     if (_canPause && _isPlaying && !_wasPaused && _isPaused && event->type() == QEvent::WindowActivate)
104     {
105         unpause();
106     }
107     else if (_canPause && _isPlaying && event->type() == QEvent::WindowDeactivate)
108     {
109         // If the game was paused before deactivating, it will be paused after activating.
110         _wasPaused = _isPaused;
111         pause();
112     }
113     return result;
114 }
115
116 void MainWindow::resizeEvent(QResizeEvent *event)
117 {
118     if (event)
119         QMainWindow::resizeEvent(event);
120
121 #if defined(MOBILE) && !defined(Q_OS_SYMBIAN)
122     if (isFullScreen())
123     {
124         ui->horizontalLayout->removeWidget(ui->btnFullscreen);
125         ui->btnFullscreen->move(width() - ui->btnFullscreen->width(), height() - ui->btnFullscreen->height());
126         ui->btnFullscreen->show();
127     }
128 #endif
129
130 #if defined(Q_OS_SYMBIAN)
131     if (height() > width())
132     {
133         ui->verticalLayout->insertWidget(0, ui->lblTime);
134         ui->btnAbout->hide();
135     }
136     else
137     {
138         ui->horizontalLayout->insertWidget(0, ui->lblTime);
139         ui->btnAbout->show();
140     }
141 #endif
142
143     if (intro != 0)
144         return;
145
146     // Scaling the graphics view's content
147
148     QSizeF oldscenesize = board->sceneRect().size();
149
150     if (SettingsDialog::enableScaling())
151     {
152         // Calculating scale ratio
153         qreal oldScaleRatio = _currentScaleRatio;
154         QSize scaledPixmapSize = board->originalPixmapSize();
155         scaledPixmapSize.scale(ui->graphicsView->size(), Qt::KeepAspectRatio);
156         _currentScaleRatio = min<qreal>((qreal)scaledPixmapSize.width() / (qreal)board->originalPixmapSize().width(),
157                                         (qreal)scaledPixmapSize.height() / (qreal)board->originalPixmapSize().height());
158
159         //qDebug() << "new size" << ui->graphicsView->size() << "ratio" << _currentScaleRatio;
160
161         // Setting scene rect and scale
162         board->setSceneRect(0, 0, ui->graphicsView->width() / _currentScaleRatio, ui->graphicsView->height() / _currentScaleRatio);
163         if (0.98 < _currentScaleRatio && 1.02 > _currentScaleRatio)
164             ui->graphicsView->resetTransform();
165         else
166             ui->graphicsView->scale(_currentScaleRatio / oldScaleRatio, _currentScaleRatio / oldScaleRatio);
167     }
168     else
169     {
170         ui->graphicsView->resetTransform();
171         board->setSceneRect(0, 0, ui->graphicsView->width(), ui->graphicsView->height());
172     }
173
174     // Making sure every piece is visible and has a nice position
175     QSizeF p = (board->sceneRect().size() - oldscenesize) / 2;
176     foreach (QGraphicsItem *item, board->items())
177     {
178         QPointF newPos(item->pos().x() + p.width(),
179                        item->pos().y() + p.height());
180         item->setPos(newPos);
181         if (JigsawPuzzleItem *jpi = dynamic_cast<JigsawPuzzleItem*>(item))
182         {
183             if (jpi->canMerge())
184                 jpi->verifyPosition();
185         }
186     }
187
188     if (event)
189         event->accept();
190     //qDebug() << "current scale ratio is" << _currentScaleRatio;
191 }
192
193 void MainWindow::showHighScores()
194 {
195     highscores->exec();
196 }
197
198 void MainWindow::newGame()
199 {
200     if (!_isPlaying)
201     {
202         if (!QDialog::Accepted == newgame->exec())
203             return;
204
205         int rows = SettingsDialog::rows(), cols = SettingsDialog::columns();
206         _isPlaying = true;
207         _canPause = false;
208
209         if ((SettingsDialog::startInFullscreen() && !isFullScreen()))// || !SettingsDialog::startInFullscreen() && isFullScreen())
210             toggleFullscreen();
211
212         applyViewportSettings();
213
214         QPixmap pixmap;
215         if (pixmap.load(newgame->getPictureFile()) && !pixmap.isNull())
216         {
217             ui->btnOpenImage->hide();
218             ui->actionNew_game->setVisible(false);
219             if (!isFullScreen())
220             {
221                 ui->btnSurrender->show();
222                 ui->actionSurrender->setVisible(true);
223             }
224
225             if (SettingsDialog::useAccelerometer())
226                 fixCurrentOrientation();
227             _isPaused = false;
228
229             QProgressDialog *progress = new QProgressDialog(tr("Generating puzzle..."), tr("Cancel"), 0, rows * cols, this);
230             progress->setWindowTitle(tr("Generating puzzle..."));
231 #ifndef MOBILE
232             if (rows * cols < 20)
233 #endif
234                 progress->show();
235
236             if (!intro.isNull())
237                 intro->hide();
238
239             if (board != 0)
240             {
241                 board->deleteLater();
242             }
243
244             ui->graphicsView->resetTransform();
245             board = new JigsawPuzzleBoard(ui->graphicsView);
246             board->setBackgroundBrush(QBrush(SettingsDialog::boardBackground()));
247             board->setSceneRect(0, 0, ui->graphicsView->width(), ui->graphicsView->height());
248             ui->graphicsView->setScene(board);
249
250             connect(board, SIGNAL(loadProgressChanged(int)), progress, SLOT(setValue(int)));
251             connect(board, SIGNAL(gameStarted()), progress, SLOT(deleteLater()));
252             connect(board, SIGNAL(gameStarted()), this, SLOT(initializeGame()));
253             connect(board, SIGNAL(gameWon()), this, SLOT(onWon()));
254             connect(board, SIGNAL(gameEnded()), this, SLOT(endGame()));
255
256 #if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
257             if (SettingsDialog::useDropShadow())
258                 connect(board, SIGNAL(loaded()), board, SLOT(enableDropshadow()));
259 #endif
260
261             board->startGame(pixmap, rows, cols);
262             _currentScaleRatio = board->originalScaleRatio();
263             ui->graphicsView->scale(_currentScaleRatio, _currentScaleRatio);
264         }
265         else
266         {
267             QMessageBox::warning(this, tr("Error"), tr("Could not load the selected image."));
268             _isPlaying = false;
269         }
270     }
271 }
272
273 void MainWindow::surrender()
274 {
275     if (_isPlaying && QMessageBox::Yes == QMessageBox::question(this, tr("Are you sure?"), tr("Are you sure you want to abandon this game?"), QMessageBox::Yes, QMessageBox::Cancel))
276     {
277         endGame();
278
279         _isPlaying = false;
280         timer->stop();
281         board->surrenderGame();
282     }
283 }
284
285 void MainWindow::applyViewportSettings()
286 {
287 #if defined(HAVE_OPENGL)
288     if (SettingsDialog::useOpenGl() && !ui->graphicsView->viewport()->inherits("QGLWidget"))
289     {
290         ui->graphicsView->viewport()->deleteLater();
291         ui->graphicsView->setViewport(new QGLWidget(QGLFormat(QGL::DoubleBuffer), this));
292     }
293     else if (!SettingsDialog::useOpenGl() && ui->graphicsView->viewport()->inherits("QGLWidget"))
294     {
295         ui->graphicsView->viewport()->deleteLater();
296         ui->graphicsView->setViewport(new QWidget(this));
297     }
298 #endif
299 }
300
301 void MainWindow::onWon()
302 {
303     endGame();
304
305     // Showing congratulations
306 #if defined(Q_WS_MAEMO_5)
307     QMaemo5InformationBox::information(0, "<b>" + tr("You rock!") + "</b><br />" + tr("Congratulations!") + "<br />" + tr("You've successfully solved the given puzzle!"), 5000);
308 #else
309     QMessageBox::information(this, tr("You rock!"), tr("Congratulations!") + "\n" + tr("You've successfully solved the given puzzle!"));
310 #endif
311
312     // Adding to high scores
313     highscores->addResult(QString::number(SettingsDialog::columns()) + QString::fromUtf8("×") + QString::number(SettingsDialog::rows()),
314                           QDateTime::currentDateTime().toString(Qt::SystemLocaleLongDate) + " - " + QString::number(_secsElapsed) + " seconds");
315 }
316
317 void MainWindow::initializeGame()
318 {
319     _secsElapsed = 0;
320     updateElapsedTimeLabel();
321     _canPause = true;
322
323     // This is to set the correct icon on the button and to start the timer
324     unpause();
325
326 #if defined (MOBILE) && !defined(Q_OS_SYMBIAN)
327     if (!isFullScreen())
328     {
329         ui->btnPause->show();
330         ui->btnSurrender->show();
331         ui->actionPause->setVisible(true);
332     }
333 #else
334     ui->btnPause->show();
335     ui->btnSurrender->show();
336     ui->actionPause->setVisible(true);
337 #endif
338
339     // Snap tolerance
340     if (JigsawPuzzleBoard *jpb = qobject_cast<JigsawPuzzleBoard*>(board))
341         jpb->setTolerance(SettingsDialog::tolerance());
342 }
343
344 void MainWindow::endGame()
345 {
346     _isPlaying = false;
347     timer->stop();
348
349     // Additional things
350 #if defined(MOBILE) && !defined(Q_OS_SYMBIAN)
351     if (!isFullScreen())
352 #endif
353     {
354         ui->btnOpenImage->show();
355     }
356     ui->btnPause->hide();
357
358     ui->actionNew_game->setVisible(true);
359     ui->btnSurrender->hide();
360     ui->actionSurrender->setVisible(false);
361     ui->btnPause->hide();
362     ui->actionPause->setVisible(false);
363
364     if (SettingsDialog::useAccelerometer())
365     {
366         board->disableAccelerometer();
367         unfixCurrentOrientation();
368     }
369 }
370
371 void MainWindow::showSettings()
372 {
373     settings->exec();
374
375     // Board background
376     QColor bg = SettingsDialog::boardBackground();
377     if (board->backgroundBrush().color() != bg)
378     {
379         board->setBackgroundBrush(QBrush(bg));
380         if (!intro.isNull())
381             intro->setDefaultTextColor(QColor(0xFFFFFF - bg.rgb()));
382     }
383
384     // Snap tolerance
385     if (_isPlaying)
386         if (JigsawPuzzleBoard *jpb = qobject_cast<JigsawPuzzleBoard*>(board))
387             jpb->setTolerance(SettingsDialog::tolerance());
388
389 #if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
390     // Drop shadows
391     if (SettingsDialog::useDropShadow() && !board->isDropshadowActive() && intro.isNull())
392         board->enableDropshadow();
393     else if (!SettingsDialog::useDropShadow() && board->isDropshadowActive())
394         board->disableDropshadow();
395 #endif
396     // Accelerometer
397     if (_isPlaying && SettingsDialog::useAccelerometer())
398     {
399         board->enableAccelerometer();
400         fixCurrentOrientation();
401     }
402     else
403     {
404         board->disableAccelerometer();
405         unfixCurrentOrientation();
406     }
407
408     //Viewport
409     applyViewportSettings();
410 }
411
412 void MainWindow::about()
413 {
414 #if defined(Q_WS_MAEMO_5)
415     QtHeWrapper::showHeAboutDialog(this,
416                                    tr("Fun and addictive jigsaw puzzle game"),
417                                    tr("Licensed under the terms of EUPL 1.1 - 2011, Timur Kristóf"),
418                                    "http://gitorious.org/colorful-apps/pages/PuzzleMaster",
419                                    "http://talk.maemo.org/showthread.php?t=67139",
420                                    "https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=venemo%40msn%2ecom&lc=US&item_name=to%20Timur%20Kristof%2c%20for%20Puzzle%20Master%20development&currency_code=EUR&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted",
421                                    "puzzle-master");
422 #else
423     QMessageBox::information(this, tr("About"), fetchAboutString(), QMessageBox::Ok);
424 #endif
425 }
426
427 void MainWindow::updateElapsedTimeLabel()
428 {
429     if (_isPlaying)
430         _secsElapsed++;
431
432     QString str;
433
434     if (_isPaused)
435         str = tr("Game paused!");
436     else if (_secsElapsed == 1)
437         str = tr("Elapsed %1 second").arg(QString::number(_secsElapsed));
438     else
439         str = tr("Elapsed %1 seconds").arg(QString::number(_secsElapsed));
440
441 #if defined(MOBILE)
442     setWindowTitle(str);
443 #endif
444     ui->lblTime->setText(str);
445 }
446
447 void MainWindow::toggleFullscreen()
448 {
449 #if defined(Q_OS_SYMBIAN)
450 #else
451     if (isFullScreen())
452     {
453         showNormal();
454
455 #if defined(MOBILE)
456         ui->horizontalLayout->addWidget(ui->btnFullscreen);
457         ui->btnAbout->show();
458         ui->btnFullscreen->show();
459         //ui->btnHelp->show();
460         if (!_isPlaying)
461             ui->btnOpenImage->show();
462         ui->btnPause->show();
463         ui->btnSettings->show();
464         if (_isPlaying)
465             ui->btnSurrender->show();
466 #else
467         ui->menuBar->show();
468 #endif
469     }
470     else
471     {
472         showFullScreen();
473
474 #if defined(MOBILE)
475         ui->btnAbout->hide();
476         ui->btnFullscreen->hide();
477         //ui->btnHelp->hide();
478         ui->btnOpenImage->hide();
479         ui->btnPause->hide();
480         ui->btnSettings->hide();
481         ui->btnSurrender->hide();
482 #else
483         ui->menuBar->hide();
484 #endif
485     }
486
487     // This is what it takes to fully update the appearance
488     QApplication *app = (QApplication*) QApplication::instance();
489     app->processEvents();
490     update();
491     app->processEvents();
492     layout()->update();
493     app->processEvents();
494 #endif
495 }
496
497 void MainWindow::pause()
498 {
499     //qDebug() << "game paused";
500     _isPaused = true;
501     timer->stop();
502     board->disable();
503     ui->btnPause->setIcon(QIcon(QPixmap(":/unpause.png")));
504     ui->actionPause->setText(tr("Resume"));
505
506     // Stopping accelerometer
507     if (SettingsDialog::useAccelerometer())
508         board->disableAccelerometer();
509 }
510
511 void MainWindow::unpause()
512 {
513     //qDebug() << "game resumed";
514     _isPaused = false;
515     timer->start();
516     board->enable();
517     ui->btnPause->setIcon(QIcon(QPixmap(":/pause.png")));
518     ui->actionPause->setText(tr("Pause"));
519
520     // Starting accelerometer
521     if (SettingsDialog::useAccelerometer())
522         board->enableAccelerometer();
523 }
524
525 void MainWindow::togglePause()
526 {
527     if (_isPlaying)
528     {
529         if (!_isPaused)
530         {
531             pause();
532 #if defined(Q_WS_MAEMO_5)
533             QMaemo5InformationBox::information(this, "<b>" + tr("Game paused!") + "</b><br />" + tr("You now can't move the pieces."), 2000);
534 #elif defined (MOBILE)
535             QMessageBox::information(this, tr("Game paused!"), tr("You now can't move the pieces."), QMessageBox::Ok);
536 #endif
537             updateElapsedTimeLabel();
538         }
539         else
540         {
541 #if defined(Q_WS_MAEMO_5)
542             QMaemo5InformationBox::information(this, "<b>" + tr("Game resumed!") + "</b><br />" + tr("Now you can move the pieces again."), 2000);
543 #elif defined (MOBILE)
544             QMessageBox::information(this, tr("Game resumed!"), tr("Now you can move the pieces again."), QMessageBox::Ok);
545 #endif
546             updateElapsedTimeLabel();
547             unpause();
548         }
549     }
550 }
551
552 void MainWindow::fixCurrentOrientation()
553 {
554 #if defined(Q_WS_MAEMO_5)
555     if (width() > height())
556         setAttribute(Qt::WA_Maemo5LandscapeOrientation);
557     else
558         setAttribute(Qt::WA_Maemo5PortraitOrientation);
559 #elif defined(Q_OS_SYMBIAN)
560     CAknAppUi* appUi = dynamic_cast<CAknAppUi*> (CEikonEnv::Static()->AppUi());
561     if (width() > height())
562         appUi->SetOrientationL(CAknAppUi::EAppUiOrientationLandscape);
563     else
564         appUi->SetOrientationL(CAknAppUi::EAppUiOrientationPortrait);
565 #endif
566 }
567
568 void MainWindow::unfixCurrentOrientation()
569 {
570 #if defined(Q_WS_MAEMO_5)
571     setAttribute(Qt::WA_Maemo5AutoOrientation);
572 #elif defined(Q_OS_SYMBIAN)
573     CAknAppUi* appUi = dynamic_cast<CAknAppUi*> (CEikonEnv::Static()->AppUi());
574     appUi->SetOrientationL(CAknAppUi::EAppUiOrientationAutomatic);
575 #endif
576 }
577
578 void MainWindow::exitTriggered()
579 {
580     this->close();
581 }