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
This commit is contained in:
sinamas 2008-05-20 07:59:18 +00:00
parent c074b7d358
commit 43d47f32fd
7 changed files with 168 additions and 72 deletions

View File

@ -19,6 +19,7 @@
#include "gambattemenuhandler.h"
#include <QtGui>
#include <QActionGroup>
#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&cent"));
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() {
<p>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.</p>")
);
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();
}

View File

@ -20,12 +20,15 @@
#define GAMBATTEMENUHANDLER_H
#include <QObject>
#include <QList>
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<QAction*> 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[]);

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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; }
};

View File

@ -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<OsdElement>());
}
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;

View File

@ -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);