Reimplement save file logic
With this change, only the assembly source and assembled flat binary are
able to be saved. There should be no need to save the disassembled view - if so,
it should suffice to copy from the disassembled view (or use objdump).
MODIFY
src/edittab.cpp
src/edittab.cpp
@@ -122,6 +122,7 @@ void EditTab::enableAssemblyInput() {
m_activeProgram = Program();
m_ui->binaryedit->clear();
enableEditor();
+ emit editorStateChanged(true);
}
void EditTab::setDisassemblerText() {
@@ -144,6 +145,7 @@ void EditTab::disableEditor() {
disconnect(m_ui->assemblyedit, &CodeEditor::textChanged, this, &EditTab::assemble);
m_ui->editorStackedWidget->setCurrentIndex(1);
clearAssemblyEditor();
+ emit editorStateChanged(false);
}
void EditTab::on_disassembledViewButton_toggled() {

MODIFY
src/edittab.h
src/edittab.h
@@ -37,6 +37,7 @@ public:
signals:
void programChanged(const Program* program);
+ void editorStateChanged(bool enabled);
public slots:
void emitProgramChanged();

MODIFY
src/instructionmodel.h
src/instructionmodel.h
@@ -14,12 +14,18 @@ class Parser;
class Pipeline;
static inline uint32_t indexToAddress(const QModelIndex& index) {
+ if (ProcessorHandler::get()->getProgram()) {
return (index.row() * 4) + ProcessorHandler::get()->getProgram()->getSection(TEXT_SECTION_NAME)->address;
}
+ return 0;
+}
static inline unsigned addressToIndex(uint32_t addr) {
+ if (ProcessorHandler::get()->getProgram()) {
return (addr - ProcessorHandler::get()->getProgram()->getSection(TEXT_SECTION_NAME)->address) / 4;
}
+ return 0;
+}
class InstructionModel : public QAbstractTableModel {
Q_OBJECT

MODIFY
src/mainwindow.cpp
src/mainwindow.cpp
#include "processorhandler.h"
#include "processortab.h"
#include "registerwidget.h"
+#include "savedialog.h"
#include "fancytabbar/fancytabbar.h"
@@ -63,6 +64,7 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), m_ui(new Ui::Main
connect(this, &MainWindow::updateMemoryTab, m_memoryTab, &MemoryTab::update);
connect(m_stackedTabs, &QStackedWidget::currentChanged, m_memoryTab, &MemoryTab::update);
connect(m_editTab, &EditTab::programChanged, ProcessorHandler::get(), &ProcessorHandler::loadProgram);
+ connect(m_editTab, &EditTab::editorStateChanged, [=] { this->m_hasSavedFile = false; });
connect(ProcessorHandler::get(), &ProcessorHandler::reqProcessorReset, m_processorTab, &ProcessorTab::reset);
connect(ProcessorHandler::get(), &ProcessorHandler::reqReloadProgram, m_editTab, &EditTab::emitProgramChanged);
@@ -98,12 +100,15 @@ void MainWindow::setupMenus() {
auto* saveAction = new QAction(saveIcon, "Save File", this);
saveAction->setShortcut(QKeySequence::Save);
connect(saveAction, &QAction::triggered, this, &MainWindow::saveFilesTriggered);
+ connect(m_editTab, &EditTab::editorStateChanged, [saveAction](bool enabled) { saveAction->setEnabled(enabled); });
m_ui->menuFile->addAction(saveAction);
const QIcon saveAsIcon = QIcon(":/icons/saveas.svg");
auto* saveAsAction = new QAction(saveAsIcon, "Save File As...", this);
saveAsAction->setShortcut(QKeySequence::SaveAs);
connect(saveAsAction, &QAction::triggered, this, &MainWindow::saveFilesAsTriggered);
+ connect(m_editTab, &EditTab::editorStateChanged,
+ [saveAsAction](bool enabled) { saveAsAction->setEnabled(enabled); });
m_ui->menuFile->addAction(saveAsAction);
m_ui->menuFile->addSeparator();
@@ -129,6 +134,7 @@ void MainWindow::setupExamplesMenu(QMenu* parent) {
parms.filepath = QString(":/examples/assembly/") + fileName;
parms.type = FileType::Assembly;
m_editTab->loadFile(parms);
+ m_hasSavedFile = false;
});
}
}
@@ -144,6 +150,7 @@ void MainWindow::loadFileTriggered() {
return;
m_editTab->loadFile(diag.getParams());
+ m_hasSavedFile = false;
}
void MainWindow::about() {
@@ -178,65 +185,47 @@ void writeBinaryFile(QFile& file, const QByteArray& data) {
} // namespace
void MainWindow::saveFilesTriggered() {
- if (m_currentFile.isEmpty()) {
- saveFilesAsTriggered();
+ SaveDialog diag;
+ if (!m_hasSavedFile) {
+ diag.exec();
+ m_hasSavedFile = true;
}
- // if (m_ui->actionSave_Source->isChecked()) {
- {
- QFile file(m_currentFile);
+ if (!diag.assemblyPath().isEmpty()) {
+ QFile file(diag.assemblyPath());
writeTextFile(file, m_editTab->getAssemblyText());
}
- //}
- // QAction* binaryStoreAction = m_binaryStoreAction->checkedAction();
- // if (binaryStoreAction == m_ui->actionSave_as_flat_binary) {
- {
- QFile file(removeFileExt(m_currentFile) + ".bin");
+ if (!diag.binaryPath().isEmpty()) {
+ QFile file(diag.binaryPath());
writeBinaryFile(file, m_editTab->getBinaryData());
}
}
void MainWindow::saveFilesAsTriggered() {
- QFileDialog dialog(this);
- dialog.setNameFilter("*.as *.s");
- dialog.setAcceptMode(QFileDialog::AcceptSave);
- dialog.setDefaultSuffix(".s");
- dialog.setModal(true);
- if (dialog.exec()) {
- m_currentFile = dialog.selectedFiles()[0];
+ SaveDialog diag;
+ auto ret = diag.exec();
+ if (ret == QDialog::Rejected)
+ return;
+ m_hasSavedFile = true;
saveFilesTriggered();
}
-}
void MainWindow::newProgramTriggered() {
QMessageBox mbox;
mbox.setWindowTitle("New Program...");
mbox.setStandardButtons(QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);
- if (!m_editTab->getAssemblyText().isEmpty() && m_currentFile.isEmpty()) {
+ if (!m_editTab->getAssemblyText().isEmpty() || m_hasSavedFile) {
// User wrote a program but did not save it to a file yet
mbox.setText("Save program before creating new file?");
auto ret = mbox.exec();
switch (ret) {
case QMessageBox::Yes: {
- saveFilesAsTriggered();
- break;
- }
- case QMessageBox::No: {
- break;
- }
- case QMessageBox::Cancel: {
+ saveFilesTriggered();
+ if (!m_hasSavedFile) {
+ // User must have rejected the save file dialog
return;
}
- }
- } else if (!m_currentFile.isEmpty()) {
- // User previously stored a program but may have updated in the meantime - prompt to ask whether the program
- // should be stored to the current file name
- mbox.setText(QString("Save program \"%1\" before creating new file?").arg(m_currentFile));
- auto ret = mbox.exec();
- switch (ret) {
- case QMessageBox::Yes: {
- saveFilesTriggered();
break;
}
case QMessageBox::No: {
@@ -247,7 +236,8 @@ void MainWindow::newProgramTriggered() {
}
}
}
- m_currentFile.clear();
+ m_hasSavedFile = false;
m_editTab->newProgram();
-} // namespace Ripes
+}
+
} // namespace Ripes

MODIFY
src/mainwindow.h
src/mainwindow.h
@@ -48,13 +48,14 @@ private:
void loadFile(const QString& filename, FileType type);
void setupMenus();
+ void setupExamplesMenu(QMenu* parent);
Ui::MainWindow* m_ui;
- void setupExamplesMenu(QMenu* parent);
- QString m_currentFile = QString();
QActionGroup* m_binaryStoreAction;
QToolBar* m_toolbar = nullptr;
+ bool m_hasSavedFile = false;
+
// Tabs
QStackedWidget* m_stackedTabs = nullptr;
ProcessorTab* m_processorTab = nullptr;

ADD
src/savedialog.cpp
+#include "savedialog.h"
+#include "ui_savedialog.h"
+
+#include <QCheckBox>
+#include <QFileDialog>
+
+namespace Ripes {
+
+QString SaveDialog::m_path = QString();
+bool SaveDialog::m_saveAssembly = true;
+bool SaveDialog::m_saveBinary = false;
+
+SaveDialog::SaveDialog(QWidget* parent) : QDialog(parent), m_ui(new Ui::SaveDialog) {
+ m_ui->setupUi(this);
+
+ setWindowTitle("Save files...");
+
+ connect(m_ui->openFile, &QPushButton::clicked, this, &SaveDialog::openFileButtonTriggered);
+ connect(m_ui->filePath, &QLineEdit::textChanged, this, &SaveDialog::pathChanged);
+ connect(m_ui->saveBinary, &QCheckBox::toggled, this, &SaveDialog::pathChanged);
+ connect(m_ui->saveAssembly, &QCheckBox::toggled, this, &SaveDialog::pathChanged);
+
+ m_ui->saveAssembly->setChecked(m_saveAssembly);
+ m_ui->saveBinary->setChecked(m_saveBinary);
+ m_ui->filePath->setText(m_path);
+ pathChanged();
+}
+
+void SaveDialog::accept() {
+ m_saveAssembly = m_ui->saveAssembly->isChecked();
+ m_saveBinary = m_ui->saveBinary->isChecked();
+ m_path = m_ui->filePath->text();
+
+ QDialog::accept();
+}
+
+void SaveDialog::pathChanged() {
+ bool okEnabled = !m_ui->filePath->text().isEmpty();
+ okEnabled &= m_ui->saveAssembly->isChecked() | m_ui->saveBinary->isChecked();
+
+ m_ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(okEnabled);
+
+ QStringList filesToSave;
+ const QString& path = m_ui->filePath->text();
+ if (m_ui->saveAssembly->isChecked() && !path.isEmpty())
+ filesToSave << m_ui->filePath->text() + ".s";
+ if (m_ui->saveBinary->isChecked() && !path.isEmpty())
+ filesToSave << m_ui->filePath->text() + ".bin";
+
+ m_ui->filesToSave->setText(filesToSave.join("<br/>"));
+}
+
+void SaveDialog::openFileButtonTriggered() {
+ QFileDialog dialog(this);
+ dialog.setAcceptMode(QFileDialog::AcceptSave);
+ if (dialog.exec()) {
+ m_ui->filePath->setText(dialog.selectedFiles()[0]);
+ }
+}
+
+SaveDialog::~SaveDialog() {
+ delete m_ui;
+}
+
+} // namespace Ripes

ADD
src/savedialog.h
+#pragma once
+
+#include <QDialog>
+
+namespace Ripes {
+
+namespace Ui {
+class SaveDialog;
+}
+
+class SaveDialog : public QDialog {
+ Q_OBJECT
+
+public:
+ explicit SaveDialog(QWidget* parent = nullptr);
+ ~SaveDialog();
+
+ static const QString& getPath() { return m_path; }
+ static QString assemblyPath() { return m_saveAssembly ? m_path + ".s" : QString(); }
+ static QString binaryPath() { return m_saveBinary ? m_path + ".bin" : QString(); }
+
+ void accept() override;
+
+private:
+ void openFileButtonTriggered();
+ void pathChanged();
+
+ Ui::SaveDialog* m_ui;
+
+ static QString m_path;
+ static bool m_saveAssembly;
+ static bool m_saveBinary;
+};
+
+} // namespace Ripes

ADD
src/savedialog.ui
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Ripes::SaveDialog</class>
+ <widget class="QDialog" name="Ripes::SaveDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>495</width>
+ <height>159</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Dialog</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_2">
+ <item row="0" column="0">
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="1" column="1">
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QCheckBox" name="saveAssembly">
+ <property name="text">
+ <string>Assembly source</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="saveBinary">
+ <property name="text">
+ <string>Assembled binary</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Files:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>File</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLineEdit" name="filePath"/>
+ </item>
+ <item row="0" column="2">
+ <widget class="QPushButton" name="openFile">
+ <property name="text">
+ <string>Save</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Output:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QLabel" name="filesToSave">
+ <property name="text">
+ <string>dstFiles</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="1" column="0">
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>Ripes::SaveDialog</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>248</x>
+ <y>254</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>157</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>Ripes::SaveDialog</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>316</x>
+ <y>260</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>

Reimplement save file logic

With this change, only the assembly source and assembled flat binary are
able to be saved. There should be no need to save the disassembled view - if so,
it should suffice to copy from the disassembled view (or use objdump).