/* QSoloCards is a collection of Solitaire card games written using Qt Copyright (C) 2009 Steve Moore This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "mainwindow.h" #include #include #include #include #include #include #include "CardAnimationLock.h" const unsigned int MainWindow::MaxWidth=780; const unsigned int MainWindow::MaxHeight=900; const QString MainWindow::SizeStr("size"); const QString MainWindow::PtStr("point"); const QString MainWindow::HelpStr("help"); const QString MainWindow::GameIdStr("LastGame"); const QString MainWindow::AnimationStr("EnableAnimations"); ///////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////// MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), m_pGameBoard(NULL), m_pMenuBar(NULL), m_pGameOptionsMenu(NULL), m_pHelpMenu(NULL), m_settings("QSoloCards","QSoloCards"), m_pNewGameAction(NULL), m_pRestartAction(NULL), m_pUndoAction(NULL), m_pRedoAction(NULL), m_pHintAction(NULL), m_pAnimationAction(NULL), m_pDemoAction(NULL), m_pCheatAction(NULL), m_pStatusBar(NULL), m_pStatusBarLabel(NULL), m_helpWindow(NULL), m_aboutWindow(NULL), m_gameMgr(), m_firstShow(false) { // make the window a nice size and make sure it is on the screen QSize deskSize(QApplication::desktop()->size()); unsigned int width=MaxWidth<(unsigned int)deskSize.width()?MaxWidth:deskSize.width()-100; unsigned int height=MaxHeight<(unsigned int)deskSize.height()?MaxHeight:deskSize.height()-100; int locX=(deskSize.width()-width)/2; int locY=(deskSize.height()-height)/2; this->resize(m_settings.value(SizeStr, QSize(width, height)).toSize()); this->move(m_settings.value(PtStr, QPoint(locX, locY)).toPoint()); // create the status bar we will use to show the score and game info. this->m_pStatusBar=new QStatusBar; this->m_pStatusBarLabel=new QLabel; this->m_pStatusBar->addPermanentWidget(m_pStatusBarLabel); this->setStatusBar(this->m_pStatusBar); // show the last game played or the default game if we don't have a last played. // we need to create the board before adding the menus. This is done. So, the // initial check state of the game selected will reflect the initial game. int gameId=m_settings.value(GameIdStr, GameMgr::DefaultGame).toInt(); GameBoard * pGameBoard=m_gameMgr.getGame((GameMgr::GameId)gameId); // add the menus for the window this->addMenuItems(); // finally setup the board. this->setupGameBoard(pGameBoard); } ///////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////// MainWindow::~MainWindow() { // save the current settings for the game this->m_settings.beginGroup(this->m_pGameBoard->gameSettingsId()); this->m_pGameBoard->saveSettings(this->m_settings); this->m_settings.endGroup(); // save the position of the game window this->m_settings.setValue(SizeStr,this->size()); this->m_settings.setValue(PtStr,this->pos()); // save the current game being played this->m_settings.setValue(GameIdStr,this->m_gameMgr.getGameId()); // save if animation is enabled or not this->m_settings.setValue(AnimationStr,this->m_pAnimationAction->isChecked()); } ///////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////// void MainWindow::slotNewGame() { if (NULL!=this->m_pGameBoard) { this->m_pGameBoard->newGame(); } } ///////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////// void MainWindow::slotRestartGame() { if (NULL!=this->m_pGameBoard) { this->m_pGameBoard->restartGame(); } } ///////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////// void MainWindow::slotSelectGame(QAction * pAction) { if (NULL!=pAction) { bool ok=false; int gameId=pAction->data().toInt(&ok); // if we got the gameid and it is not the same as the current game // set the game as the current. if (ok && gameId!=this->m_gameMgr.getGameId()) { this->setupGameBoard(this->m_gameMgr.getGame((GameMgr::GameId)gameId)); } } } void MainWindow::slotAnimation(bool checked) { CardAnimationLock::getInst().enableAnimations(checked); if (this->m_pGameBoard->hasDemo() && checked) { this->m_pDemoAction->setEnabled(true); } else { this->m_pDemoAction->setEnabled(false); } } ///////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////// void MainWindow::slotCheat(bool checked) { if (NULL!=this->m_pGameBoard) { this->m_pGameBoard->setCheat(checked); } } ///////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////// void MainWindow::slotUndo() { if (NULL!=this->m_pGameBoard) { if (this->m_pGameBoard->canUndoMove()) { this->m_pGameBoard->undoMove(); } } } ///////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////// void MainWindow::slotRedo() { if (NULL!=this->m_pGameBoard) { if (this->m_pGameBoard->canRedoMove()) { this->m_pGameBoard->redoMove(); } } } ///////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////// void MainWindow::slotHint() { if (NULL!=this->m_pGameBoard) { this->m_pGameBoard->showHint(); } } ///////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////// void MainWindow::slotUndoAvail(bool avail) { if (this->m_pUndoAction && !m_pDemoAction->isChecked()) { this->m_pRestartAction->setEnabled(avail); this->m_pUndoAction->setEnabled(avail); } } ///////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////// void MainWindow::slotRedoAvail(bool avail) { if (this->m_pRedoAction) { this->m_pRedoAction->setEnabled(avail); } } ///////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////// void MainWindow::slotScoreChanged(int score,const QString & info) { if (this->m_pStatusBar && !m_pDemoAction->isChecked()) { QString statusMsg(tr("%1 Score: %2").arg(info).arg(QString::number(score)).trimmed()); this->m_pStatusBarLabel->setText(statusMsg); } } ///////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////// void MainWindow::slotAbout() { if (NULL==this->m_aboutWindow) { this->m_aboutWindow=new About(this); connect(this->m_aboutWindow.data(),SIGNAL(showLink(QString)), this,SLOT(slotShowHelp(QString))); } this->m_aboutWindow->show(); this->m_aboutWindow->activateWindow(); } ///////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////// void MainWindow::slotHelp() { if (this->m_pGameBoard) { this->slotShowHelp(this->m_pGameBoard->helpFile()); } } ///////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////// void MainWindow::slotHelpClosed(int finishCode) { Q_UNUSED(finishCode); this->m_settings.beginGroup(HelpStr); this->m_settings.setValue(SizeStr,this->m_helpWindow->size()); this->m_settings.setValue(PtStr,this->m_helpWindow->pos()); this->m_settings.endGroup(); } ///////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////// void MainWindow::slotShowHelp(const QString & helpFile) { if (NULL==this->m_helpWindow) { this->m_helpWindow=new Help(this,helpFile); this->m_settings.beginGroup(HelpStr); this->m_helpWindow->resize(this->m_settings.value(SizeStr,QSize(640,480)).toSize()); this->m_helpWindow->move(this->m_settings.value(PtStr,QPoint(200,200)).toPoint()); this->m_settings.endGroup(); this->connect(this->m_helpWindow,SIGNAL(finished(int)),this,SLOT(slotHelpClosed(int))); this->m_helpWindow->show(); } else { this->m_helpWindow->setHelpFile(helpFile); this->m_helpWindow->show(); this->m_helpWindow->activateWindow(); } } ///////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////// void MainWindow::slotAnimationStarted() { m_pRestartAction->setEnabled(false); m_pDemoAction->setEnabled(false); } ///////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////// void MainWindow::slotAnimationComplete() { m_pRestartAction->setEnabled(true); m_pDemoAction->setEnabled(true); } ///////////////////////////////////////////////////////////////////////////////////////////// // this timeout is just to delay slightly calling newGame for a board. So, the // animation will start with the QGraphicsScene visible and the board will already be // completely drawn. ///////////////////////////////////////////////////////////////////////////////////////////// void MainWindow::slotNewGameTimer() { if (this->m_pGameBoard) { this->m_pMenuBar->setEnabled(true); this->m_pGameBoard->newGame(); } } ///////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////// void MainWindow::slotDemoStarted() { m_pNewGameAction->setEnabled(false); m_pRestartAction->setEnabled(false); m_pUndoAction->setEnabled(false); m_pRedoAction->setEnabled(false); m_pHintAction->setEnabled(false); m_pAnimationAction->setEnabled(false); m_pGameSelectMenu->setEnabled(false); m_pGameOptionsMenu->setEnabled(false); m_pCheatAction->setEnabled(false); m_pDemoAction->setChecked(true); } ///////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////// void MainWindow::slotDemoStopped() { m_pNewGameAction->setEnabled(true); m_pRestartAction->setEnabled(true); m_pHintAction->setEnabled(true); m_pAnimationAction->setEnabled(true); m_pGameSelectMenu->setEnabled(true); m_pGameOptionsMenu->setEnabled(true); m_pCheatAction->setEnabled(true); if (this->m_pGameBoard->canUndoMove()) { m_pUndoAction->setEnabled(true); } if (this->m_pGameBoard->canRedoMove()) { m_pRedoAction->setEnabled(true); } m_pDemoAction->setChecked(false); } ///////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////// void MainWindow::slotDemoActionItem(bool checked) { if (this->m_pGameBoard) { if (checked) { this->m_pGameBoard->startDemo(); } else { this->m_pGameBoard->stopDemo(); } } } ///////////////////////////////////////////////////////////////////////////////////////////// // we are just using the showEvent to make sure that we don't start the animation of the dealing // of cards for the game until the window has been shown. ///////////////////////////////////////////////////////////////////////////////////////////// void MainWindow::showEvent(QShowEvent * pShowEvent) { Q_UNUSED(pShowEvent); if (!m_firstShow && NULL!=this->m_pGameBoard) { this->m_pGameBoard->newGame(); m_firstShow=true; } } ///////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////// void MainWindow::addMenuItems() { // create the menu for the app. this->m_pMenuBar=new QMenuBar(this); // create a game menu QMenu * pGameMenu=new QMenu(tr("Game").trimmed(),this->m_pMenuBar); m_pNewGameAction=new QAction(tr("New Game").trimmed(),pGameMenu); m_pNewGameAction->setShortcuts(QKeySequence::New); this->connect(m_pNewGameAction,SIGNAL(triggered()), this,SLOT(slotNewGame())); pGameMenu->addAction(m_pNewGameAction); this->m_pRestartAction=new QAction(tr("Restart Game").trimmed(),pGameMenu); this->m_pRestartAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_R)); this->connect(this->m_pRestartAction,SIGNAL(triggered()), this,SLOT(slotRestartGame())); pGameMenu->addAction(this->m_pRestartAction); pGameMenu->addSeparator(); this->m_pGameSelectMenu=new QMenu(tr("Select Game").trimmed(),pGameMenu); // add the available games to the menu. this->m_gameMgr.buildGameListMenu(*this->m_pGameSelectMenu); // connecting this one up a little different. Since, we are building // a generic menu where we don't know what the contents are connect up // to the menus triggered event. We can then get the action and from it's // user data the id of the game selected. this->connect(this->m_pGameSelectMenu,SIGNAL(triggered(QAction*)), this,SLOT(slotSelectGame(QAction*))); pGameMenu->addMenu(this->m_pGameSelectMenu); #if !(defined Q_WS_MAC) pGameMenu->addSeparator(); #endif QAction * pQuit=new QAction(tr("Quit").trimmed(),pGameMenu); pQuit->setMenuRole(QAction::QuitRole); pQuit->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Q)); this->connect(pQuit,SIGNAL(triggered()), this,SLOT(close())); pGameMenu->addAction(pQuit); this->m_pMenuBar->addMenu(pGameMenu); /////////////////////////////////////////////////////////////////////////// // create a control menu // for the cheat and undo menu items /////////////////////////////////////////////////////////////////////////// QMenu * pCtrlMenu=new QMenu(tr("Control").trimmed(),this->m_pMenuBar); m_pUndoAction=new QAction(tr("Undo").trimmed(),pCtrlMenu); m_pUndoAction->setShortcuts(QKeySequence::Undo); this->connect(m_pUndoAction,SIGNAL(triggered()), this,SLOT(slotUndo())); pCtrlMenu->addAction(m_pUndoAction); m_pRedoAction=new QAction(tr("Redo").trimmed(),pCtrlMenu); m_pRedoAction->setShortcuts(QKeySequence::Redo); this->connect(m_pRedoAction,SIGNAL(triggered()), this,SLOT(slotRedo())); pCtrlMenu->addAction(m_pRedoAction); m_pHintAction=new QAction(tr("Hint").trimmed(),pCtrlMenu); m_pHintAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_H)); this->connect(m_pHintAction,SIGNAL(triggered()), this,SLOT(slotHint())); pCtrlMenu->addAction(m_pHintAction); pCtrlMenu->addSeparator(); m_pAnimationAction=new QAction(tr("Animation").trimmed(),pCtrlMenu); m_pAnimationAction->setCheckable(true); this->connect(m_pAnimationAction,SIGNAL(triggered(bool)), this,SLOT(slotAnimation(bool))); pCtrlMenu->addAction(m_pAnimationAction); m_pAnimationAction->setChecked(m_settings.value(AnimationStr, true).toBool()); CardAnimationLock::getInst().enableAnimations(m_pAnimationAction->isChecked()); m_pDemoAction=new QAction(tr("Demo").trimmed(),pCtrlMenu); m_pDemoAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_D)); m_pDemoAction->setCheckable(true); this->connect(m_pDemoAction,SIGNAL(triggered(bool)), this,SLOT(slotDemoActionItem(bool))); pCtrlMenu->addAction(m_pDemoAction); pCtrlMenu->addSeparator(); m_pCheatAction=new QAction(tr("Cheat").trimmed(),pCtrlMenu); m_pCheatAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_C)); m_pCheatAction->setCheckable(true); this->connect(m_pCheatAction,SIGNAL(triggered(bool)), this,SLOT(slotCheat(bool))); pCtrlMenu->addAction(m_pCheatAction); this->m_pMenuBar->addMenu(pCtrlMenu); /////////////////////////////////////////////////////////////////////////// // create a game options menu // this menu will get the specific game option settings to add to the menu // from the GameBoard class. We will not add it to the menu at this time. // It will be added and/or removed when the game board is setup. It will // only be added if it is needed. /////////////////////////////////////////////////////////////////////////// m_pGameOptionsMenu=new QMenu(tr("&Options").trimmed(),this->m_pMenuBar); //////////////////////////////////////////////////////////////////////////// // add the help menu //////////////////////////////////////////////////////////////////////////// this->m_pHelpMenu=new QMenu(tr("&Help").trimmed(),this->m_pMenuBar); QAction * pHelp=new QAction(tr("Game Help").trimmed(),this->m_pHelpMenu); pHelp->setShortcut(QKeySequence(Qt::Key_F1)); this->connect(pHelp,SIGNAL(triggered()), this,SLOT(slotHelp())); this->m_pHelpMenu->addAction(pHelp); #if !(defined Q_WS_MAC) this->m_pHelpMenu->addSeparator(); #endif QAction * pAbout=new QAction(tr("&About").trimmed(),this->m_pHelpMenu); pAbout->setMenuRole(QAction::AboutRole); this->connect(pAbout,SIGNAL(triggered()), this,SLOT(slotAbout())); this->m_pHelpMenu->addAction(pAbout); this->m_pMenuBar->addMenu(this->m_pHelpMenu); this->setMenuBar(this->m_pMenuBar); // also go ahead and hook up the slots for the signals that animation is in progress // now only used by the restart game menu item. Need the undo stack to be updated before // we can restart a game. connect(&CardAnimationLock::getInst(),SIGNAL(animationStarted()), this,SLOT(slotAnimationStarted())); connect(&CardAnimationLock::getInst(),SIGNAL(animationComplete()), this,SLOT(slotAnimationComplete())); } ///////////////////////////////////////////////////////////////////////////////////////////// // Set a gameboard and hook up the menus, set the icon, etc... for the new board // if there was a previous gameboard. When the QMainWindow::setCentralWidget function is called // the old game board will be deleted by Qt. ///////////////////////////////////////////////////////////////////////////////////////////// void MainWindow::setupGameBoard(GameBoard * pGameBoard) { if (pGameBoard) { // save settings of the current game if we have one if (NULL!=this->m_pGameBoard) { this->m_settings.beginGroup(this->m_pGameBoard->gameSettingsId()); this->m_pGameBoard->saveSettings(this->m_settings); this->m_settings.endGroup(); } this->m_pGameBoard=pGameBoard; // connect the gameboards score changed signal to our slot so we // can get score changes and display them. But first clear the // current contents of the statusbar to get ride of old info if necessary. this->m_pStatusBar->showMessage(""); this->m_pStatusBarLabel->setText(""); this->connect(this->m_pGameBoard,SIGNAL(scoreChanged(int,QString)), this,SLOT(slotScoreChanged(int,QString))); this->m_settings.beginGroup(this->m_pGameBoard->gameSettingsId()); this->m_pGameBoard->loadSettings(m_settings); this->m_settings.endGroup(); this->setCentralWidget(this->m_pGameBoard); this->setWindowTitle(tr("QSoloCards: %1").arg(this->m_pGameBoard->gameName()).trimmed()); // connect up the game board to the menus m_pUndoAction->setEnabled(this->m_pGameBoard->canUndoMove()); m_pRedoAction->setEnabled(this->m_pGameBoard->canRedoMove()); m_pRestartAction->setEnabled(this->m_pGameBoard->canUndoMove()); this->m_pMenuBar->removeAction(this->m_pGameOptionsMenu->menuAction()); this->m_pGameOptionsMenu->clear(); this->m_pGameBoard->addGameMenuItems(*this->m_pGameOptionsMenu); if (!this->m_pGameOptionsMenu->isEmpty()) { this->m_pMenuBar->insertMenu(this->m_pHelpMenu->menuAction(),this->m_pGameOptionsMenu); } // set the window icon for the game. this->setWindowIcon(this->m_pGameBoard->getGamePixmap()); /////////////////////////////////////////////////////////////////////////// // connect up the slots so we will know when undo and redo actions are available /////////////////////////////////////////////////////////////////////////// this->connect(this->m_pGameBoard,SIGNAL(undoAvail(bool)), this,SLOT(slotUndoAvail(bool))); this->connect(this->m_pGameBoard,SIGNAL(redoAvail(bool)), this,SLOT(slotRedoAvail(bool))); if (this->isVisible()) { // disable the menus while we wait for the timer to pop. this->m_pMenuBar->setEnabled(false); QTimer::singleShot(100,this,SLOT(slotNewGameTimer())); } this->m_pDemoAction->setChecked(false); if (this->m_pGameBoard->hasDemo() && CardAnimationLock::getInst().animationsEnabled()) { this->m_pDemoAction->setEnabled(true); this->connect(this->m_pGameBoard,SIGNAL(demoStarted()), this,SLOT(slotDemoStarted())); this->connect(this->m_pGameBoard,SIGNAL(demoStopped()), this,SLOT(slotDemoStopped())); } else { this->m_pDemoAction->setEnabled(false); } } }