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:
parent
c074b7d358
commit
43d47f32fd
@ -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¢"));
|
||||
|
||||
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();
|
||||
}
|
||||
|
@ -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[]);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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; }
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user