From 43d47f32fd8d8f9db80a29f5c93c0432ab820417 Mon Sep 17 00:00:00 2001 From: sinamas Date: Tue, 20 May 2008 07:59:18 +0000 Subject: [PATCH] add save state actions to GUI menu clean up GUI menu creation code move GUI recent files to submenu move state 0 OSD pos to rightmost to match kbd layout state 1 default on ROM load support external save state files add number key slot selection shortcuts git-svn-id: https://gambatte.svn.sourceforge.net/svnroot/gambatte@145 9dfb2916-2d38-0410-aef4-c5fe6c9ffc24 --- gambatte_qt/src/gambattemenuhandler.cpp | 161 +++++++++++++++--------- gambatte_qt/src/gambattemenuhandler.h | 14 ++- gambatte_qt/src/gambattesource.h | 8 ++ gambatte_sdl/src/gambatte_sdl.cpp | 10 ++ libgambatte/include/gambatte.h | 4 + libgambatte/src/gambatte.cpp | 41 ++++-- libgambatte/src/state_osd_elements.cpp | 2 +- 7 files changed, 168 insertions(+), 72 deletions(-) diff --git a/gambatte_qt/src/gambattemenuhandler.cpp b/gambatte_qt/src/gambattemenuhandler.cpp index 77fab619..7effedd5 100644 --- a/gambatte_qt/src/gambattemenuhandler.cpp +++ b/gambatte_qt/src/gambattemenuhandler.cpp @@ -19,6 +19,7 @@ #include "gambattemenuhandler.h" #include +#include #include "palettedialog.h" #include "mainwindow.h" #include "gambattesource.h" @@ -50,87 +51,85 @@ GambatteMenuHandler::GambatteMenuHandler(MainWindow *const mw, GambatteSource *c } { - QAction *openAct = new QAction(tr("&Open..."), mw); - openAct->setShortcut(tr("Ctrl+O")); - openAct->setStatusTip(tr("Open an existing file")); - connect(openAct, SIGNAL(triggered()), this, SLOT(open())); - for (int i = 0; i < MaxRecentFiles; ++i) { recentFileActs[i] = new QAction(mw); recentFileActs[i]->setVisible(false); connect(recentFileActs[i], SIGNAL(triggered()), this, SLOT(openRecentFile())); } - resetAct = new QAction(tr("&Reset"), mw); - resetAct->setShortcut(tr("Ctrl+R")); - resetAct->setEnabled(false); - connect(resetAct, SIGNAL(triggered()), recentFileActs[0], SLOT(trigger())); - QMenu *fileMenu = mw->menuBar()->addMenu(tr("&File")); - fileMenu->addAction(openAct); - separatorAct = fileMenu->addSeparator(); + fileMenu->addAction(tr("&Open..."), this, SLOT(open()), tr("Ctrl+O")); - for (int i = 0; i < MaxRecentFiles; ++i) - fileMenu->addAction(recentFileActs[i]); + { + recentMenu = fileMenu->addMenu(tr("Open Re¢")); + + for (int i = 0; i < MaxRecentFiles; ++i) + recentMenu->addAction(recentFileActs[i]); + } fileMenu->addSeparator(); - fileMenu->addAction(resetAct); + romLoadedActions.append(fileMenu->addAction(tr("&Reset"), recentFileActs[0], SLOT(trigger()), tr("Ctrl+R"))); fileMenu->addSeparator(); - QAction *exitAct = new QAction(tr("E&xit"), mw); - exitAct->setShortcut(tr("Ctrl+Q")); - exitAct->setStatusTip(tr("Exit the application")); - connect(exitAct, SIGNAL(triggered()), qApp, SLOT(closeAllWindows())); + romLoadedActions.append(fileMenu->addAction(tr("Save State &As..."), this, SLOT(saveStateAs()))); + romLoadedActions.append(fileMenu->addAction(tr("Load State &From..."), this, SLOT(loadStateFrom()))); + fileMenu->addSeparator(); - fileMenu->addAction(exitAct); + romLoadedActions.append(fileMenu->addAction(tr("&Save State"), source, SLOT(saveState()), QString("F5"))); + romLoadedActions.append(fileMenu->addAction(tr("&Load State"), source, SLOT(loadState()), QString("F8"))); + + { + stateSlotMenu = fileMenu->addMenu(tr("S&elect State Slot")); + stateSlotMenu->setEnabled(false); + stateSlotMenu->addAction(tr("&Previous"), this, SLOT(prevStateSlot()), QString("F6")); + stateSlotMenu->addAction(tr("&Next"), this, SLOT(nextStateSlot()), QString("F7")); + stateSlotMenu->addSeparator(); + + stateSlotGroup = new QActionGroup(mw); + + for (int i = 0; i < 10; ++i) { + const int no = i == 9 ? 0 : i + 1; + const QString &strno = QString::number(no); + QAction *action = stateSlotMenu->addAction("Slot &" + strno, this, SLOT(selectStateSlot()), strno); + + action->setCheckable(true); + action->setData(no); + stateSlotGroup->addAction(action); + } + } + + foreach(QAction *a, romLoadedActions) { + a->setEnabled(false); + } + + fileMenu->addSeparator(); + + fileMenu->addAction(tr("E&xit"), qApp, SLOT(closeAllWindows()), tr("Ctrl+Q")); updateRecentFileActions(); } QMenu *settingsm = mw->menuBar()->addMenu(tr("&Settings")); - { - QAction *const act = new QAction(tr("&Input..."), mw); - connect(act, SIGNAL(triggered()), mw, SLOT(execInputDialog())); - settingsm->addAction(act); - } - - { - QAction *const act = new QAction(tr("&Sound..."), mw); - connect(act, SIGNAL(triggered()), mw, SLOT(execSoundDialog())); - settingsm->addAction(act); - } - - { - QAction *const act = new QAction(tr("&Video..."), mw); - connect(act, SIGNAL(triggered()), mw, SLOT(execVideoDialog())); - settingsm->addAction(act); - } + settingsm->addAction(tr("&Input..."), mw, SLOT(execInputDialog())); + settingsm->addAction(tr("&Sound..."), mw, SLOT(execSoundDialog())); + settingsm->addAction(tr("&Video..."), mw, SLOT(execVideoDialog())); settingsm->addSeparator(); { QMenu *const palm = settingsm->addMenu(tr("DMG &Palette")); - { - QAction *act = new QAction(tr("&Global..."), mw); - connect(act, SIGNAL(triggered()), this, SLOT(execGlobalPaletteDialog())); - palm->addAction(act); - } + palm->addAction(tr("&Global..."), this, SLOT(execGlobalPaletteDialog())); - romPaletteAct = new QAction(tr("Current &ROM..."), mw); + romPaletteAct = palm->addAction(tr("Current &ROM..."), this, SLOT(execRomPaletteDialog())); romPaletteAct->setEnabled(false); - connect(romPaletteAct, SIGNAL(triggered()), this, SLOT(execRomPaletteDialog())); - palm->addAction(romPaletteAct); } settingsm->addSeparator(); { - QAction *fsAct = new QAction(tr("&Full Screen"), mw); - fsAct->setShortcut(tr("Ctrl+F")); + QAction *fsAct = settingsm->addAction(tr("&Full Screen"), mw, SLOT(toggleFullScreen()), tr("Ctrl+F")); fsAct->setCheckable(true); - connect(fsAct, SIGNAL(triggered()), mw, SLOT(toggleFullScreen())); - settingsm->addAction(fsAct); } // settingsm->addAction(hideMenuAct); @@ -138,9 +137,7 @@ GambatteMenuHandler::GambatteMenuHandler(MainWindow *const mw, GambatteSource *c mw->menuBar()->addSeparator(); QMenu *helpMenu = mw->menuBar()->addMenu(tr("&Help")); - QAction *aboutAct = new QAction(tr("&About"), mw); - connect(aboutAct, SIGNAL(triggered()), this, SLOT(about())); - helpMenu->addAction(aboutAct); + helpMenu->addAction(tr("&About"), this, SLOT(about())); mw->addActions(mw->menuBar()->actions()); @@ -177,7 +174,7 @@ void GambatteMenuHandler::updateRecentFileActions() { for (int j = numRecentFiles; j < MaxRecentFiles; ++j) recentFileActs[j]->setVisible(false); - separatorAct->setVisible(numRecentFiles > 0); + recentMenu->setEnabled(numRecentFiles > 0); } void GambatteMenuHandler::setCurrentFile(const QString &fileName) { @@ -221,7 +218,13 @@ void GambatteMenuHandler::loadFile(const QString &fileName) { romPaletteAct->setEnabled(!source->isCgb()); setCurrentFile(fileName); - resetAct->setEnabled(true); + + foreach(QAction *a, romLoadedActions) { + a->setEnabled(true); + } + + stateSlotMenu->setEnabled(true); + stateSlotGroup->actions().at(0)->setChecked(true); mw->run(); } @@ -229,13 +232,14 @@ void GambatteMenuHandler::loadFile(const QString &fileName) { void GambatteMenuHandler::open() { mw->pause(); - QString fileName = QFileDialog::getOpenFileName(mw, "Open", recentFileActs[0]->data().toString(), "Game Boy ROM images (*.dmg *.gb *.gbc *.sgb *.zip);;All files (*)"); + const QString &fileName = QFileDialog::getOpenFileName(mw, tr("Open"), recentFileActs[0]->data().toString(), + tr("Game Boy ROM Images (*.dmg *.gb *.gbc *.sgb *.zip);;All Files (*)")); if (!fileName.isEmpty()) loadFile(fileName); mw->unpause(); - mw->setFocus(); + mw->setFocus(); // giving back focus after getOpenFileName seems to fail at times, which can be problematic with current exclusive mode handling. } void GambatteMenuHandler::openRecentFile() { @@ -246,6 +250,8 @@ void GambatteMenuHandler::openRecentFile() { } void GambatteMenuHandler::about() { + const bool wasPaused = mw->isPaused(); + mw->pause(); QMessageBox::about( @@ -257,7 +263,8 @@ void GambatteMenuHandler::about() {

Gambatte is an accuracy-focused, open-source, cross-platform Game Boy / Game Boy Color emulator written in C++. It is based on hundreds of corner case hardware tests, as well as previous documentation and reverse engineering efforts.

") ); - mw->unpause(); + if (!wasPaused) + mw->unpause(); } void GambatteMenuHandler::globalPaletteChange() { @@ -283,3 +290,43 @@ void GambatteMenuHandler::execGlobalPaletteDialog() { void GambatteMenuHandler::execRomPaletteDialog() { mw->execDialog(romPaletteDialog); } + +void GambatteMenuHandler::prevStateSlot() { + stateSlotGroup->actions().at(source->currentState() < 2 ? source->currentState() + 8 : source->currentState() - 2)->trigger(); +} + +void GambatteMenuHandler::nextStateSlot() { + stateSlotGroup->actions().at(source->currentState())->trigger(); +} + +void GambatteMenuHandler::selectStateSlot() { + if (QAction *action = stateSlotGroup->checkedAction()) + source->selectState(action->data().toInt()); +} + +void GambatteMenuHandler::saveStateAs() { + const bool wasPaused = mw->isPaused(); + + mw->pause(); + + const QString &fileName = QFileDialog::getSaveFileName(mw, tr("Save State"), QString(), tr("Gambatte Quick Save Files (*.gqs);;All Files (*)")); + + if (!fileName.isEmpty()) { + source->saveState(fileName.toAscii().data()); + } + + if (!wasPaused) + mw->unpause(); +} + +void GambatteMenuHandler::loadStateFrom() { + mw->pause(); + + const QString &fileName = QFileDialog::getOpenFileName(mw, tr("Load State"), QString(), tr("Gambatte Quick Save Files (*.gqs);;All Files (*)")); + + if (!fileName.isEmpty()) { + source->loadState(fileName.toAscii().data()); + } + + mw->unpause(); +} diff --git a/gambatte_qt/src/gambattemenuhandler.h b/gambatte_qt/src/gambattemenuhandler.h index e11e7a5c..d0806408 100644 --- a/gambatte_qt/src/gambattemenuhandler.h +++ b/gambatte_qt/src/gambattemenuhandler.h @@ -20,12 +20,15 @@ #define GAMBATTEMENUHANDLER_H #include +#include class MainWindow; class GambatteSource; class QAction; class PaletteDialog; class QString; +class QActionGroup; +class QMenu; class GambatteMenuHandler : public QObject { Q_OBJECT @@ -35,11 +38,13 @@ class GambatteMenuHandler : public QObject { MainWindow *const mw; GambatteSource *const source; QAction *recentFileActs[MaxRecentFiles]; - QAction *separatorAct; - QAction *resetAct; + QMenu *recentMenu; QAction *romPaletteAct; PaletteDialog *globalPaletteDialog; PaletteDialog *romPaletteDialog; + QActionGroup *stateSlotGroup; + QMenu *stateSlotMenu; + QList romLoadedActions; void loadFile(const QString &fileName); void setCurrentFile(const QString &fileName); @@ -54,6 +59,11 @@ private slots: void romPaletteChange(); void execGlobalPaletteDialog(); void execRomPaletteDialog(); + void prevStateSlot(); + void nextStateSlot(); + void selectStateSlot(); + void saveStateAs(); + void loadStateFrom(); public: GambatteMenuHandler(MainWindow *mw, GambatteSource *source, int argc, const char *const argv[]); diff --git a/gambatte_qt/src/gambattesource.h b/gambatte_qt/src/gambattesource.h index 0ab19332..5d95b5d2 100644 --- a/gambatte_qt/src/gambattesource.h +++ b/gambatte_qt/src/gambattesource.h @@ -65,6 +65,10 @@ public: void setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned rgb32) { gb.setDmgPaletteColor(palNum, colorNum, rgb32); } void setSavedir(const std::string &sdir) { gb.set_savedir(sdir.c_str()); } bool isCgb() const { return gb.isCgb(); } + void selectState(int n) { gb.selectState(n); } + int currentState() const { return gb.currentState(); } + void saveState(const char *filepath) { gb.saveState(filepath); } + void loadState(const char *filepath) { gb.loadState(filepath); } //overrides void buttonPressEvent(unsigned buttonIndex); @@ -74,6 +78,10 @@ public: void setVideoSource(unsigned videoSourceIndex) { gb.setVideoFilter(videoSourceIndex); } void update(unsigned samples); +public slots: + void saveState() { gb.saveState(); } + void loadState() { gb.loadState(); } + signals: void blit(); void setTurbo(bool on); diff --git a/gambatte_sdl/src/gambatte_sdl.cpp b/gambatte_sdl/src/gambatte_sdl.cpp index 14884c15..48361849 100644 --- a/gambatte_sdl/src/gambatte_sdl.cpp +++ b/gambatte_sdl/src/gambatte_sdl.cpp @@ -653,6 +653,16 @@ int GambatteSdl::exec() { case SDLK_F6: gambatte.selectState(gambatte.currentState() - 1); break; case SDLK_F7: gambatte.selectState(gambatte.currentState() + 1); break; case SDLK_F8: gambatte.loadState(); break; + case SDLK_0: gambatte.selectState(0); break; + case SDLK_1: gambatte.selectState(1); break; + case SDLK_2: gambatte.selectState(2); break; + case SDLK_3: gambatte.selectState(3); break; + case SDLK_4: gambatte.selectState(4); break; + case SDLK_5: gambatte.selectState(5); break; + case SDLK_6: gambatte.selectState(6); break; + case SDLK_7: gambatte.selectState(7); break; + case SDLK_8: gambatte.selectState(8); break; + case SDLK_9: gambatte.selectState(9); break; default: break; } } diff --git a/libgambatte/include/gambatte.h b/libgambatte/include/gambatte.h index 9e3c6408..37a52fb0 100644 --- a/libgambatte/include/gambatte.h +++ b/libgambatte/include/gambatte.h @@ -33,6 +33,8 @@ class GB { CPU *const z80; int stateNo; + void loadState(const char *filepath, bool osdMessage); + public: GB(); ~GB(); @@ -55,6 +57,8 @@ public: bool isCgb() const; void saveState(); void loadState(); + void saveState(const char *filepath); + void loadState(const char *filepath); void selectState(int n); int currentState() const { return stateNo; } }; diff --git a/libgambatte/src/gambatte.cpp b/libgambatte/src/gambatte.cpp index 1f7b1070..b058084c 100644 --- a/libgambatte/src/gambatte.cpp +++ b/libgambatte/src/gambatte.cpp @@ -42,7 +42,7 @@ static const std::string statePath(const std::string &basePath, int stateNo) { } namespace Gambatte { -GB::GB() : z80(new CPU), stateNo(0) {} +GB::GB() : z80(new CPU), stateNo(1) {} GB::~GB() { delete z80; @@ -107,6 +107,9 @@ bool GB::load(const char* romfile) { setInitState(state, z80->isCgb()); z80->loadState(state); z80->loadSavedata(); + + stateNo = 1; + z80->setOsdElement(std::auto_ptr()); } return failed; @@ -124,26 +127,40 @@ void GB::setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned rgb32) z80->setDmgPaletteColor(palNum, colorNum, rgb32); } -void GB::saveState() { - SaveState state; - z80->setStatePtrs(state); - z80->saveState(state); - StateSaver::saveState(state, statePath(z80->saveBasePath(), stateNo).c_str()); - z80->setOsdElement(newStateSavedOsdElement(stateNo)); -} - -void GB::loadState() { +void GB::loadState(const char *const filepath, const bool osdMessage) { z80->saveSavedata(); SaveState state; z80->setStatePtrs(state); - if (StateSaver::loadState(state, statePath(z80->saveBasePath(), stateNo).c_str())) { + if (StateSaver::loadState(state, filepath)) { z80->loadState(state); - z80->setOsdElement(newStateLoadedOsdElement(stateNo)); + + if (osdMessage) + z80->setOsdElement(newStateLoadedOsdElement(stateNo)); } } +void GB::saveState() { + saveState(statePath(z80->saveBasePath(), stateNo).c_str()); + z80->setOsdElement(newStateSavedOsdElement(stateNo)); +} + +void GB::loadState() { + loadState(statePath(z80->saveBasePath(), stateNo).c_str(), true); +} + +void GB::saveState(const char *filepath) { + SaveState state; + z80->setStatePtrs(state); + z80->saveState(state); + StateSaver::saveState(state, filepath); +} + +void GB::loadState(const char *const filepath) { + loadState(filepath, false); +} + void GB::selectState(int n) { n -= (n / 10) * 10; stateNo = n < 0 ? n + 10 : n; diff --git a/libgambatte/src/state_osd_elements.cpp b/libgambatte/src/state_osd_elements.cpp index 5f8edc84..b384ef56 100644 --- a/libgambatte/src/state_osd_elements.cpp +++ b/libgambatte/src/state_osd_elements.cpp @@ -137,7 +137,7 @@ public: }; SaveStateOsdElement::SaveStateOsdElement(const char *fileName, unsigned stateNo) : -OsdElement(stateNo * ((160 - StateSaver::SS_WIDTH) / 10) + ((160 - StateSaver::SS_WIDTH) / 10) / 2, 4, StateSaver::SS_WIDTH, StateSaver::SS_HEIGHT), +OsdElement((stateNo ? stateNo - 1 : 9) * ((160 - StateSaver::SS_WIDTH) / 10) + ((160 - StateSaver::SS_WIDTH) / 10) / 2, 4, StateSaver::SS_WIDTH, StateSaver::SS_HEIGHT), life(4 * 60) { std::ifstream file(fileName);